
自Django第3版正式发布以来已经过去了几周。 甚至在正式版本发布之前,我就使用该版本。不幸的是,我注意到Django的开发速度大大降低了。 版本1.3有时与1.7不同,但是版本3包含对分支2的外观更改,仅此而已。
我的winePad项目始于Django 1.3,到目前为止,它已经重新定义了Django内部代码的12%。
看到新版本的代码后,我了解到我或我的同事在使用旧版本时所做的编辑将会更进一步。 并且查看路线图和官方存储库中的缓慢更改,您不必等待将来的版本中纠正错误。
这就是我要谈论的关于错误的工作:
获取方法
很少有人意识到从一开始标准get django方法就存在错误。 get方法应该将一个对象返回给您,或者警告已找到多个对象,或者报告没有对象。
1 def get(self, *args, **kwargs): 2 clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) 3 if self.query.can_filter() and not self.query.distinct_fields: 4 clone = clone.order_by() 5 limit = None 6 if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit: 7 limit = MAX_GET_RESULTS 8 clone.query.set_limits(high=limit) 9 num = len(clone) 10 if num == 1: 11 return clone._result_cache[0] 12 if not num: 13 raise self.model.DoesNotExist() 14 raise self.model.MultipleObjectsReturned()
第9行接收queryset中指定的所有记录上的数据,并将它们转换为一组对象。 文档中对此有警告。
在版本3之前,请求的对象数没有限制。 这意味着在发出警告说有很多对象之前,绝对要接收所有数据并将它们变成对象。
结果,您可以在内存中获得数百万个对象,而只是发现发现了多个对象。 现在有第5、7、8行。 现在,您自豪地只得到MAX_GET_RESULTS = 21个对象,然后才知道有1个以上的对象。
如果使用沉重的“ __init__”,则延迟将很明显。 如何治疗:
覆盖django.db.models.query.py中的MAX_GET_RESULTS
覆盖GET或在调用GET使用之前:
vars(queryset.query).update({'high_mark':2, 'low_mark':0}) queryset.query.set_limits(0,2)
Django模型的__Init__方法
__init__内置方法的代码中的声明并不十分清楚
_setattr=setattr
这可能是通过将函数引用转移到本地字典来伪加速代码的,但这不是关于此的。 有几个问题:
1.如果在__init__模型中传递了附加的attribute = value对,您将得到“得到一个意外的关键字参数”。
在这种情况下,我建议不要负担__init__方法,而要在初始化后添加属性:
obj = MyClass() vars(obj).update({'attr':val, 'attr2':val2 ...})
2.新的Django添加了在模型的任何字段(Field.descriptor_class)上重新定义描述符的功能。 但是,没有一个描述符知道对象是否已初始化。 例如,如果描述符将使用来自prefetch_related对象的数据(仅在主对象初始化之后才出现),则这是必需的。
我不想使用信号来完成初始化,因为可能会有很多订阅者。
在这种情况下,我没有想出比重写__init__和添加属性结束初始化更聪明的方法了。
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._end_init = True
查询集
硬编码的EmptyQuerySet / DateQuerySet已删除,已经很好了。 但是,从思想上讲,以queryset作为管理器的情况我并不喜欢。
如果要覆盖管理器创建的QuerySet类,请添加_queryset_class属性
class MyManager(models.Manager): _queryset_class = MyQuerySet
注意,对于较旧的版本,此功能不起作用,例如,您可以执行以下操作:
class MyManager(models.Manager): def get_query_set(self): response = super(MyManager, self).get_query_set() response.__class__ = MyQuerySet return response
inlineFormset管理面板
有几个问题:
1.无法显示与主表单对象没有直接连接的标准inlineFormset记录。 例如:用于编辑产品价格的表单。 在中间的是“类似”商品批发价格的参考统计表格形式。
通过重写get_formset内联模型方法并创建从BaseInlineFormSet继承的自己的MyFormSet可以解决此问题。
def get_formset(self, request, obj=None, **kwargs): kwargs['formset'] = MyFormSet super().get_formset(request, obj, **kwargs) class MyFormSet(BaseInlineFormSet): pass
2.如果您正在管理面板中使用inlineformset编辑一个对象,并且此时有人将通过另一种机制删除inlineformset内部的一个对象记录,则会收到错误消息,并且您将无法保存该对象。 仅通过kopy粘贴在新的浏览器窗口中。
到目前为止,我只找到一种解决方案-不要使用inlineformset。
管理面板
Django的“杀手级功能”是该项目最大的仙人掌:
1.默认情况下,模型管理员中的“删除对象”操作是可见的,用户是否有权删除都无关紧要。
通过默认禁用此操作可以解决此问题:
admin.site.disable_action('delete_selected')
2.在启用模型管理员权限之前,无法从管理面板创建其他用户权限:
from django.contrib.auth.models import Permission class PermissionsAdmin(admin.ModelAdmin): search_fields = ('name', 'codename','content_type__app_label', 'content_type__model') list_display = ('name', 'codename',) actions = None admin.site.register(Permission, PermissionsAdmin)
3. A,在Django中不存在仅访问某些对象的权利。
这可以通过使用特殊标志将条目写入djangoAdminLog来解决。
然后检查标志:
user.logentry_set.filter(action_flag=ENABLED, .....).exists()
4.如果按照文档中的说明创建管理员操作,请记住,它们不会自动记录在djangoAdminLog中。
5.该文档这部分的另一个缺点是所有示例都只针对函数。 但是GCBV呢? 在我的项目中,模型管理员的所有操作均已转移到GCBV。
仓库。动作连接是标准的:
class MyAdmin(admin.ModelAdmin): actions = (MyActionBasedOnActionView.as_view(),)
ContentType-Django模型注册表
50%天才/ 50%迟钝。
没有一个模型可以访问默认模型注册表。
在我们的项目中,可以通过向所有类添加mixin来解决该问题:
from django.contrib.contenttypes.models import ContentType class ExportMixin(object): @classmethod def ct(cls): if not hasattr(cls, '_ct'): cls._ct, create = ContentType.objects.get_or_create(**cls.get_app_model_dict()) if create: cls._ct.name = cls._ct.model._meta.verbose_name cls._ct.save() return cls._ct @classmethod def get_model_name(cls): if not hasattr(cls, '_model_name'): cls._model_name = cls.__name__.lower() return cls._model_name @classmethod def get_app_name(cls): if not hasattr(cls, '_app_name'): cls._app_name = cls._meta.app_label.lower() return cls._app_name @classmethod def get_app_model_dict(cls): if not hasattr(cls, '_format_kwargs'): cls._format_kwargs = {'app_label': cls.get_app_name(), 'model': cls.get_model_name()} return cls._format_kwargs
现在我们可以根据需要调用obj.ct()。
用户模型
覆盖用户模型的功能出现在1.5版中。
但是对于版本3,他们没有在标准UserCreationForm / UserChangeForm中修复model = User。
解决了:
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import get_user_model class MyUserCreationForm(UserCreationForm): class Meta: model= get_user_model()
翻译系统
用户可见的标签被标记
{% trans %}
或通过代码
gettext_lazy
但是,管理面板中没有对这些资源的管理。 一般而言。
有外部解决方案,它们都以某种方式起作用。
例如,罗塞塔(Rosetta)系统地丢失了文本,并且界面出现故障。 没有任何地方对翻译的访问权的验证。 需要系统的makemessages / compilationmess才能工作...
在winePad中,trans,blocktrans和gettext_lazy标签被重新定义,我们开始从缓存中接收文本。 如果没有缓存,则来自get_or_create基的缓存请求也从makemessages中保存了我们。
多种语言的话题通常很复杂。 Django的内置解决方案仅适用于静态文本。 但是仍然需要转换这些模型。 我以自己的方式尝试解决了
django-TOF项目中翻译动态文本的问题,在该项目中,我结合了模型翻译和Parler / Hvad的功能。 可能有人会对偷看感兴趣。
现在,我将停止叙述,因为Django bug修复文章很容易变成经文。
请告诉我您如何改进Django。 如果有延续,我将所提出的想法系统化。
ps有些代码是用旧的符号编写的,希望能引起理解,并非总是能找到重构的时间或人员。