2024-07-11 | DjangoCon Europe 2024 | Modernizing CRUD Operations in Django
现代Django开发:使用Declarative Interface与Django Ninja CRUD优化CRUD操作
标签
媒体详情
- 上传日期
- 2025-06-21 18:52
- 来源
- https://www.youtube.com/watch?v=r8yRxZPcy9k
- 处理状态
- 已完成
- 转录状态
- 已完成
- Latest LLM Model
- gemini-2.5-pro
转录
speaker 1: Thank you. Thank you. Thank you. Yeah, so that's start, as you said, it's about trying to modernize even more application using jungle ninja. So let's start about how I'm going to present it. So first we are going to see jungle ninja. If you don't know it yet, what are the challenges that I faced with large scale applications? Then my vision and kind of potential solutions, spojungle, ninjkra and detailed examples. Also how I tried also to not only improve the developer experience in order to develop endpoints, but also the testing experience. And then finally the future directions, conclusion and maybe final thoughts. So first, who am who am I? So is it working? Yeah. So first, Yeah I grew up in Normandy in France, the north, and then I moved at Toulouse, other south for my studies. And I stayed in order to work at pixela. It's a little startup with a couple of friends where we are developing AI tools for computer vision. And in my free times, I'll bring football, music, painting, and developing, of course, and use the web or mobile applications and recently, open source. So you see with two packages, and for now, Yeah, it's been five years that I've been using jungle and jungle ninja for two years. Yeah. So next introduction to djungle ninja. If you didn't see it two years ago, Vitaly kushyai, the creator of djgo ninja, made an introduction, but it was remotely since because of the pandemic and stuff. So a little summary about it. I'll all of you maybe know. Like it's kind of first api developer experience where you have easy serialization using bidentic schema and an open api documentation like automatically. It's not a big deal since you can do it with other frameworks too, like jungle rest framework, but it's kind of cool to have it. And let's start the fact note. I mean, it's a joke because it really like what I'm trying to say. It really depends on you. I mean, each one of you can have some preferences. It's not like framework is better than anyone is. Okay. So for example, what do I personally love about jungle ninja? It's the abstraction level, like taking this serialization and this alization layer just a little bit higher so you don't have to do it in your g function. So Yeah, it's done in the middle way layer. So Yeah, I really like it. You just have to define Yeah just like the example. I don't know if you can see code re adsanow, but Yeah just an example. So it's visual. And Yeah, I'm not just talking like that. So Yeah so what are then if the you know the developer experience is quite increased with jungle ninja, what are the problems that I faced? That's okay. Just like any frameworks you would be using, you have a certain moment, a lot of models, a lot of repetitions in the services that are using it, updating it and also at the end point. So for the services service layer, it's quite easy. I mean, it just function and you're doing quite everything you want. With before endpoints, I find it quite difficult. I mean, even though I was referred to my code all the way I wanted with the services, I had a lot of repetition on the enpoint player two. So duplicate duplicated logic, sorry, increases, of course, the risk of inconsistencies. So my next question was, how can I fix all of those problems? How can I minimize as I've written or play code and repetitive endpoints? How can I do it in order two? Yeah, just be less frustrating when you are developing all of this stuff and when I don't know, sapeople are asking you a new vertical in your application. You have to repeat a lot of stuff and how to not just rush. So Yeah, a lot of questions that I try to basically try to respond it. And all of this started one year ago when I was vacation in palramada mariorcaso. It's kind of fun that it was in Spain and back here. So Yeah, I was trying to think about how could I fix all those problems? So one of those he was okay, there's some components, some sorry, some views that was repeated a lot all over my application. Sometimes it was, for example, a concrete one, the read operation. I had a lot of get pentions over an object that was repeated for different schemees. Sometimes you just want a little bit information, sometimes more, sometimes only ids and listing ids of specific objects. You have a lot of revisions and existing solutions like view sets in jungle reprimox, for example, didn't offer the flexibility of defining several crowaction, for example, than you had with inarritgance, only one possible implementation of each view. So I was thinking about how to how can I treat them as components like an notion, a word you obviously often see in front end development, but we are only taking the good side of it, right? The JavaScript and the key specifications include Yeah so plabased view components, even though it doesn't exist in djungo ninja, and that's not exactly what I'm trying to do or implement, like expose class based views for every people using jungle ninjaba, just that I need it in order to declare my views as modular as I ever wanted to. And Yeah so easy of creation of cruise. The goal is to ease the way of the way for people to create their own modular views. It depends on your specific your specific business logic is not always repetitive Fred and points out everyone ever saw. So first, how to create these potential core objects. So as you may know, Yeah the basic way of creating a gate operation, for example, in order to read, let's say a department object is really a basic object, right? Just have an idea, an autophill and a title you want to get it with as simple as chema. You just get the object. Okay? From this, if you check the code of the get method on the router, you see that it's just really about api operation with methods get, okay? And from it, if you read the code of jungle ninja, you see that it's just wrapping up the method at api operation in a decorator. So from it, okay, I could create an object called api view, make the method path all the attributes in it, and abstract away the view function that I could just return with a key method called as as operation as a dict, and use it when calling router api operation with the return rases as a dictionary. So we have a method to your way to define an object, realize it and use it in order to add the operation. So we just need the router, the object, and we can add a view. But then how to compose it in what I called api view sets, how to manage them and group them with, I don't know, maybe unsurely with I know Yeah similar properties like model, of course. So Yeah, first I had a look at insaclass. It's kind of in it for instances, but for classes. So as soon as you're enherrating this api view set mail, I can access all the surplus, all the class atates of it, and especially the instance, the subclasses of api view. So you just have to iterate from api view, put it as a class attribute to api view set, and then I can just iterate over it though to change, of course, the view function name two, because it's going to be generic, of course, to the class attribute name. So at the same time, I'm dodging like the conflict possible with view function name. I'm then accessing the ipr view set from each modular view so you can access similar like same properties on the api view set in the module views and then call the function the method Add View to that is doing what I just talked about previously, like serializing your api view in aoperation ation and call the router when but then it was a little bit too abstract. Like I could not expose this to every one of you, every users, and just say, okay, let's go. You can create now your own api views. I had to take some really fundamentals that were both repetitive and common to everyone. And thanks. There's like some advice. Go create, read, update and delete the crowd that was first interested in 1983 by James Martin. I hope you maybe read the book, but Yeah don't like I refound with those the exact kind of empress I needed in order to showcase how to declare your views are model or like as components. So it was kind of the introduction of the package so I could expose it to everyone, make it understanding understanding and see exactly directly how you're gonna know how you could have the declarative and intuitive syntax to kind of every endpoints you see easily with crud and you can easily think about it for your repetitive and common logic inside your proper business logic. So Yeah, just like that, I have defined like five classical classic views and be able to compose my view set as I ever wanted, even, for example, use the list views several times. As I said, for example, you can list a department with a simple schema and an extended one with related objects and a simple one with just the ids. You can do whatever you want, but you identified correctly that those endpoints were kind of the same and could be possibly refactor at the top level. I mean, at the definition. Okay, little bit to abstract. So everycode, what is directly doing? What we are writing it, you just see that I've leveraged the inherritance of values in the api view set. So you just have, for example, you know that the view set linked to department object is going to have views that are going to use the models. If I access it and access the default scheas, I could easily create list sts, create read and update delete views. So just like that in several lines of code, you just abstract a way kind of the botle plate code you know to expose a new model that you just integrated in your application. And Yeah, a keypart d is that rethe same in same views, in the same view sets. So for example, if you have migration of schema, for example, the head of the exposition of your new field, or rename a field for certain in clients, you could just add A V two path and just expose the same schema without the need to rewrite another endpoint. So Yeah, again, a lot of abstract talks. So we need concrete examples like what does it really look like? But first, some Vanya Jungo ninja code. So you see when you are reading a classic list endpoints, for example, I'm going to do exactly the same for all the five endpoints you have the basic one, let's say it's your proper business logic endpoint, right? Not a credit endpoint. You have to identify around all the use of it what's really similar in order to find the perfect interface. Like it's a general problem. Okay? You have to define your own function. I could not do that, but I kind of done it for basic credit d operations. So Yeah, once you found the basic request components, maybe some method around it. Paonage class, of course, even though I put a default. Like jungle ninja has done with limit offset paginations. So Yeah, you could easily write some complex endpoints, but also really easy to read. For example, here I'm easily defining a list. Employeendpoint is really just a list related to department. Yeah I didn't introduce the jungle models, but you can easily know that employee as a foreign key to department object. So you're from direct path where I know the ID is from the model defined, that is in the view set and in the view that Yeah, the ID is the one from department. And then you can easily define your schemas and have your hand point. So we declaative and kind of really usable one time. Again, I'm really defining the tools to define it. You are free to not use the once I've like the product views my implementations of crowviews, you can extend them really easily. The key here is that you just have to define them as class attributes. So if you are creating your own custom view is going to be another class. You don't have to change the api view set. You just have to added it as the class attributes. And just like that, have your you compose your view set as I've said. And Yeah, you can do the same for the other one, like create what's changing like in it model. Maybe you're asking why because sometimes you could have like with so with the listing employees, some path paraters like, okay, if you specifying ID and then something else or even name or even any path parayou ever wanted, there's going to be some past managers and maybe you are going to use it in your Indian model, for example, even the request in order to link. I don't know if you are doing the create by user Yeah and other presave and post save like I proved to do it and you maybe should do it here in the endpoint o in the service layer instead of the signals and joinside the models. But Yeah, since it was kind of a little bit opinionated to force users to have the full clean method, I put it in a prere save that you can easily override if you want to use directly implementations of my views here at creview. And one time again, Yeah it's in the blue one are always written like the request components. Yeah. So once again, you can easily define some complex but same view. You just have to define a little bit more parameters. So Yeah, as I said in the init model method, I know if you're very far from the screen, you can see the code, but I share the slides right after the init model. You just have to rebuild the past generators ID in the department ID in order to, as I said, override the init model. So you have everything you can want to and we can do the same. But Yeah ask keep this part because I think you understood the idea. You just identify what could be customized in order to be used. We largely and minimize the code wherever it's possible. So I've done the same for updates and then you have it. And Yeah, something I just talked about previously was the possibility of defining just a new schema for the same operation really easily by just specifying the path, like just path ID V2 and you have it, just link it to the right schemas and finally delete kind of the same. I suppose, predelete and positive operations and a get model that I never never overwritten, but I'm just exposing it in order to say that Yeah, I'm allowing any kind of customization, but you're free to do whatever you ever want and Yeah, maybe not more. Yeah and let's get it. How Yeah some key component in the package is how can I abstract away the link and know which parameters it is of what type? It's really easy. When you have the path and the model, you can easily extract all the strings from your path, like the path narators name and linked it with your model in order to know exactly which field is it. So you have kind of accessing get field from the modest meta arguments and then you have it, you can dynamically generate, sorry, around time, your pdentic schema. And Yeah the keyparts, as I said, you can easily add your custom views because it is about like creating some component like views for you proper to your business logic. Not only credit d operations, so Yeah, I've talked about it, but how to do it? Yeah, again, a lot of code, I hope, really far away from the screen is okay. As I said, you just have to enter it from the api view class where you define the interface you want to. So it's probably it to your needs. But I've done an example using the subdelete operations where you could do it with an update, but it's not an update. And sometimes you wanna Yeah just define your repetitive operations and you just have to define an inner view function. And Yeah, it's just easy at that and you can use it with one line everywhere you want across several view sets all inside the same one. So no, if you make rating schemas, as I said, so Yeah, really easy to define your own one. Just use api view set and your custom view and it's okay. So Yeah, now that have showcased all the features kind of of the package, what's coming after of course, a little bit of fix because I have one issue that abstract too much the api view class. I've made it too opinionated around the crowd operations. Like I've really made it with as inputs only three parameters the request one pathway body when you could potentially have fried edder or cookie. Like it's kind of a light to say that today we can compose any custom view, but it's going be recent like here matter of weeks before I got this. And Yeah of course, implement the most ask feature, like showcase how to create some I think views to Yeah make people happy about it and you know just be on the trend. And then I couldn't maybe think about A V one that I'm waiting since one year and maybe provide some other common credit operations even though it's maybe not. The goal of this package is to showcase how to do it, but not like use all the tourists I will provide in the future and maybe extend the vision of the framework. What does that mean is at first, I really wanted to make this available. As I said, it does not deepend on the framework. So maybe adapt it easily to jungle rest framework or first api or anything just in order to defend some composable approach. But I don't know yet. I just see the V1 and then we are going to see but Yeah, my experience here didn't stop too trying to enhance the developer experience on the specific task of defining endpoints. I also tried to tackle the testing experience was a little bit harder because Yeah, it was a lot of also repetitive. At first I was really nave because I tried to kind of repeat the same pattern, like having some component like test objects. While it kind of sounded quite right at first, one year after, I would never do that again while because it was just hidden, it was just Yeah some code was hidden. You were not probably aware of what you were testing all the your applications just using a random test component like object. And it will not take the responsibility of saying to you that, okay, you are really testing everything. If you put this in your test, it's okay. I was not really convinced then. It was really bringing a lot of more problems there solutions. So Yeah, that's what kind of the thing I try to do, like link the wow, Oh, sorry, three actually, Yeah, but I tried to link all the tests together like that with the names. It was kind of working, but as I said, kind of lack of flexibility and kind of really rigid. So Yeah, it was not. No generalization was possible because I was constraining the status code and stuff. Yeah, I just skiit to you because it was really a mess. The important part was is that I successfully like flatten all the eras of made and just throw them away with an object called scenario where I'm just it's really just the black box with inpurequest components. And expected statement on your response for now, it's really basic, just abstract away the need to do the htp code and the use of the client. But Yeah, I find this interface way more easy in order to manage all you scaled tests over your big application. And Yeah, I really like the way it goes. Only one test for specific dpoint where we you are testing a lot of scenarios together. And the key e here is that you can test it, for example, with several users with different permissions and on different objects. And every scenario are completely isolated with a database fallback as I've written. And Yeah, it was so after the ration and the complete rewrite of and inspiration of jungonninja, it was just depending on jungle. So I was like, okay, maybe it does not have a spot like in this package. So I separated it even though it's kind, I say a little bit small, but Yeah, it's the little brother of jungle in jakra. I've named jungle rest St testing. And Yeah, take a look. So Yeah, that's a conclusion. Yeah, it was real time consuming to do all of those, the two packages, the maintenance and think about the features kind of alone. So it was really hard and time consuming, even though it's a really valuable learning experience. Yeah, I've improved the developer experience for me. So why not for everyone? I hope this is going to work. So Yeah, I just want to thanks all the staggezer contributors and sponsors of the packages. Really great. Without you, it would have been a lot of more difficult. Of course, thanks to GitHub, rme and jade brains for the open source license, even though GitHub is kind of natural, but we are kind of not aware of all the advantages of GitHub. And of course, thank you, John, who can organize us for.
最新摘要 (详细摘要)
概览/核心摘要 (Executive Summary)
本次演讲由开发者 Hicham Bakri 主讲,核心内容围绕他为解决 Django (特别是 Django Ninja) 大规模应用中 CRUD (创建、读取、更新、删除) 操作代码重复问题而开发的开源库 Django Ninja CRUD。Bakri 指出,传统方式下,为不同模型创建大量相似的 API 端点会导致代码冗余和不一致性风险。他的解决方案是引入一种声明式的、基于组件的接口来定义 API 视图。
该方案的核心是 APIView 和 APIViewSet 两个类。APIView 将单个端点的逻辑(如路径、方法、处理函数)封装成可复用的组件。APIViewSet 则作为一个容器,通过类属性自动发现并注册其中定义的多个 APIView 组件,从而将相关端点聚合管理。这种方法极大地简化了代码,开发者仅需几行代码即可为一个模型生成一套完整的 CRUD 端点。其关键优势在于高度的灵活性和可组合性,例如,允许为同一模型轻松定义多个具有不同序列化结构(Schema)的列表视图。
此外,Bakri 还分享了他在优化测试体验方面的探索。他最初尝试将组件化思想应用于测试,但发现效果不佳。最终,他开发了另一个独立的库 Django Rest Testing,采用基于“场景 (Scenario)”的黑盒测试方法,将请求与预期响应封装起来,从而简化了测试用例的编写和管理。最后,他展望了 Django Ninja CRUD 的未来,包括增加异步视图支持、修复现有问题,并可能将其设计理念扩展到其他框架。
Django Ninja 的挑战与机遇
-
Django Ninja 简介与优势:
- 演讲者 Hicham Bakri 首先肯定了 Django Ninja 带来的优秀开发者体验,特别是其基于 Pydantic 的便捷序列化和自动生成的 OpenAPI 文档。
- 他个人尤其欣赏其抽象层次,它将序列化和反序列化操作提升到中间件层,使视图函数(view function)本身更加纯粹和简洁。
-
面临的核心挑战:代码重复:
- 在任何大型应用中,随着模型数量的增多,不可避免地会出现大量重复的代码。
- Bakri 发现,即使业务逻辑被封装在服务层(Service Layer),在端点层(Endpoint Layer)的重复问题依然严重。
- 他强调:> "重复的逻辑...当然会增加不一致的风险 (duplicated logic... increases, of course, the risk of inconsistencies)。"
- 他认为 Django REST Framework 中的
ViewSets等现有解决方案虽然有用,但在灵活性上有所欠缺,通常只允许“对每个视图有一种可能的实现 (one possible implementation of each view)”,无法满足他更灵活的需求。
核心解决方案:声明式的组件化视图
-
设计理念与灵感:
- 该想法诞生于一年前的假期,其核心是将 API 视图视为组件 (Components),借鉴了前端开发的模块化思想。
- 目标是创建模块化、可复用的视图,以减少样板代码(boilerplate code)和开发过程中的挫败感。
-
技术实现思路:
APIView类: 通过分析 Django Ninja 的源码,他设计了一个APIView类,用于将单个 API 操作的属性(如path,method等)和视图逻辑封装成一个独立的对象。APIViewSet类: 这是一个容器类,它利用 Python 的元类特性(原文提及insaclass,应指__init_subclass__)来自动发现其内部定义为类属性的所有APIView实例。- 自动注册:
APIViewSet会遍历这些APIView组件,并将它们动态地注册到 Django Ninja 的路由器(Router)上,从而实现了视图的自动分组和声明式定义。
Django Ninja CRUD 库详解
-
基于 CRUD 的标准化:
- 为了使抽象的组件化概念更易于理解和使用,Bakri 以业界公认的 CRUD 操作(创建、读取、更新、删除)作为基础,提供了预设的视图组件。
- 这些预设组件包括
ListView,CreateView,ReadView,UpdateView,DeleteView。
-
核心优势与用法:
- 代码极简: 开发者只需继承
APIViewSet并指定模型(Model),即可用极少的代码为该模型生成一套完整的 CRUD 端点。 - 高度的灵活性与组合性: 这是该库最关键的特性。开发者可以在同一个
APIViewSet中多次使用同一种视图组件。- 示例: > "你可以多次使用列表视图。例如,你可以用一个简单的 schema 列出一个部门,用一个包含关联对象的扩展 schema 列出,还可以用一个只包含 ID 的简单 schema 列出。(you can use the list views several times. For example, you can list a department with a simple schema and an extended one with related objects and a simple one with just the ids.)"
- 强大的可定制性:
- 预设的视图组件提供了丰富的钩子(hooks)以便注入自定义业务逻辑,例如
pre_save,post_save,pre_delete,post_delete等,用户无需重写整个视图。 - 可以通过覆盖
init_model等方法,处理复杂的路径参数或请求依赖。
- 预设的视图组件提供了丰富的钩子(hooks)以便注入自定义业务逻辑,例如
- 动态路径参数生成: 该库能够智能地解析 URL 路径中的参数,并与 Django 模型字段关联,从而在运行时动态生成 Pydantic schema。
- 代码极简: 开发者只需继承
-
创建自定义视图:
- 该库的设计初衷不仅限于 CRUD。用户可以轻松创建符合自己业务需求的、可复用的自定义视图。
- 只需继承
APIView基类,定义好接口和内部视图函数,然后将其作为类属性添加到APIViewSet中即可,无需修改APIViewSet的核心代码。
测试体验的现代化
-
失败的尝试:
- Bakri 最初尝试将组件化思想也应用到测试中,创建了所谓的“测试对象 (test objects)”。
- 他很快发现这是一个错误的方向,因为它隐藏了测试细节,使得测试变得僵化,难以理解和维护。> "你可能根本不知道你正在测试什么 (You were not probably aware of what you were testing)。"
-
新的解决方案:
Django Rest Testing:- 他吸取教训,开发了一个名为
Django Rest Testing的独立库。 - 该库采用黑盒测试方法,引入了一个
Scenario对象。 Scenario对象封装了一次测试的输入(请求组件)和预期输出(响应断言)。- 这种方法使得在一个测试函数中可以清晰地定义和运行多个完全隔离的场景(例如,使用不同权限的用户进行测试),极大地提高了测试的可读性和可维护性。
- 他吸取教训,开发了一个名为
未来方向与结论
-
Django Ninja CRUD的未来计划:- 近期修复: 解决
APIView在输入参数上过于“固执己见 (opinionated)”的问题,增加对header和cookie等参数的全面支持。 - 功能增强: 实现社区呼声很高的功能,特别是对异步视图 (async views) 的支持。
- 版本规划: 考虑发布
v1.0版本。 - 长远愿景: 探索将这种可组合的视图设计理念移植到其他框架的可能性,如 Django REST Framework 或 FastAPI,但这尚无明确计划。
- 近期修复: 解决
-
个人感悟与致谢:
- Bakri 坦言,独立开发和维护这两个库是“非常耗时 (real time consuming)”的,但也是一次“非常有价值的学习经历 (really valuable learning experience)”。
- 他最后感谢了所有为项目做出贡献的人、赞助商、GitHub、JetBrains 以及 DjangoCon Europe 的组织者。