Django - 如何在不重复自己的情况下将 "None" 从 url kwargs 转换为 None
Django - How to cast "None" from url kwargs to None without repeating yourself
我有这样一个模型:
#models.py
class Location(BaseArticle):
name = models.CharField(max_length=200)
parent_location = models.ForeignKey("self",
blank=True,
null=True,
help_text="Fill in if this location is a smaller part of another location.",
on_delete=models.SET_NULL)
description = HTMLField(blank=True,
null=True,
help_text="A description of the location and important features about it")
class Meta:
unique_together = ('name', 'parent_location')
我决定在我的 url 中通过自己的名称及其父元素的名称来唯一标识每个位置。
# urls.py
urlpatterns = [
path('locations/<str:parent_location_name>/<str:location_name>', wiki_views.LocationView.as_view(), name='location'),
]
# example url to location "Seedy Bar" located in "City"
# https://<DOMAIN>/locations/city/seedy bar
# example url to location "City" that does not have a parent_location
# https://<DOMAIN>/locations/none/city
正如您从示例中看到的,parent_location
为 null/None 的位置是可能的。我决定将这些 url 的 None
转换为 "None"
,尽管我没有设置 None
转换成什么。现在你可能意识到这意味着每当我对 Location 做任何事情时,我需要注意根据需要在视图中将 "None"
转换为 None
,这很快就会变得很烦人。
为了关注 DRY,我想知道处理这个问题的最佳方法是什么,还是我把自己设计成了一个角落?
编辑:我认为最好的解决方案可能是在 Location 模型或其管理器中。在那里修复它意味着为所有视图和查询处理此逻辑,而在视图级别解决此问题将意味着需要为每个可能使用“[=44 查询 Location 的视图实施该解决方案=]".
到目前为止我想到了什么:
鉴于管理器是您与模型交互的界面,我检查了它们是否有任何内容并无意中修改了 get_queryset。但是,由于我自己不调用 get_queryset,所以我不确定是否有像 filter/exclude 这样的函数,我什至不知道是否真的调用了它。但是,我通常最喜欢这种方法,因为它保留了如何使用 model/manager 处理这些东西的逻辑,它属于我的眼中。
另一种选择当然是编写我自己的 Mixin 以在视图级别修复此问题,但这意味着每次我仍然需要记住这是一个问题。
我会这样做:
创建两个 URL:
path('locations/<str:location_name>', wiki_views.LocationView.as_view(), name='location'),
path('locations/<str:parent_location_name>/<str:location_name>', wiki_views.LocationViewWithParent.as_view(), name='location'),
并像这样构造这两个视图:
class LocationView(generics.APIView):
parent = None
... rest of your code ...
class LocationViewWithparent(LocationView):
parent = #Your code to get the parent_location_name parameter
第二个视图将执行与父视图相同的所有操作,但会覆盖父变量
多亏了@Aayush Agrawal 的建议,我才勉强避免了在这方面犯下的错误做法。事后看来,我同意,为此使用管理器不是一个好主意,因为虽然这可能有效,但它也会混淆输入并在将来产生意想不到的结果。
因此我选择了 Mixin 解决方案并实现了以下内容:
# settings.py
NONE_STRING = 'None'
# views.py
from django.conf import settings
class ReplaceNoneMixin(object):
"""Mixin to deal with URL Parameters that represent None. This mixin takes the name of a url parameter and assumes
that if it represents None that it has the value of settings.NONE_STRING. If the parameter is encountered with that
value, the value is changed from NONE_STRING to None."""
kwargs: dict
noneable_parameters_keys: list
def setup(self, request, *args, **kwargs):
"""Changes the value of all url parameters specified in noneable_parameters_keys list. If the parameter has the
settings.NONE_STRING value in kwargs then the value is set to None."""
super().setup(request, *args, **kwargs)
if isinstance(self.noneable_parameters_keys, str):
self.noneable_parameters_keys = [self.noneable_parameters_keys]
for parameter in self.noneable_parameters_keys:
if parameter in self.kwargs and self.kwargs[parameter] == settings.NONE_STRING:
self.kwargs[parameter] = None
我有这样一个模型:
#models.py
class Location(BaseArticle):
name = models.CharField(max_length=200)
parent_location = models.ForeignKey("self",
blank=True,
null=True,
help_text="Fill in if this location is a smaller part of another location.",
on_delete=models.SET_NULL)
description = HTMLField(blank=True,
null=True,
help_text="A description of the location and important features about it")
class Meta:
unique_together = ('name', 'parent_location')
我决定在我的 url 中通过自己的名称及其父元素的名称来唯一标识每个位置。
# urls.py
urlpatterns = [
path('locations/<str:parent_location_name>/<str:location_name>', wiki_views.LocationView.as_view(), name='location'),
]
# example url to location "Seedy Bar" located in "City"
# https://<DOMAIN>/locations/city/seedy bar
# example url to location "City" that does not have a parent_location
# https://<DOMAIN>/locations/none/city
正如您从示例中看到的,parent_location
为 null/None 的位置是可能的。我决定将这些 url 的 None
转换为 "None"
,尽管我没有设置 None
转换成什么。现在你可能意识到这意味着每当我对 Location 做任何事情时,我需要注意根据需要在视图中将 "None"
转换为 None
,这很快就会变得很烦人。
为了关注 DRY,我想知道处理这个问题的最佳方法是什么,还是我把自己设计成了一个角落?
编辑:我认为最好的解决方案可能是在 Location 模型或其管理器中。在那里修复它意味着为所有视图和查询处理此逻辑,而在视图级别解决此问题将意味着需要为每个可能使用“[=44 查询 Location 的视图实施该解决方案=]".
到目前为止我想到了什么:
鉴于管理器是您与模型交互的界面,我检查了它们是否有任何内容并无意中修改了 get_queryset。但是,由于我自己不调用 get_queryset,所以我不确定是否有像 filter/exclude 这样的函数,我什至不知道是否真的调用了它。但是,我通常最喜欢这种方法,因为它保留了如何使用 model/manager 处理这些东西的逻辑,它属于我的眼中。
另一种选择当然是编写我自己的 Mixin 以在视图级别修复此问题,但这意味着每次我仍然需要记住这是一个问题。
我会这样做:
创建两个 URL:
path('locations/<str:location_name>', wiki_views.LocationView.as_view(), name='location'),
path('locations/<str:parent_location_name>/<str:location_name>', wiki_views.LocationViewWithParent.as_view(), name='location'),
并像这样构造这两个视图:
class LocationView(generics.APIView):
parent = None
... rest of your code ...
class LocationViewWithparent(LocationView):
parent = #Your code to get the parent_location_name parameter
第二个视图将执行与父视图相同的所有操作,但会覆盖父变量
多亏了@Aayush Agrawal 的建议,我才勉强避免了在这方面犯下的错误做法。事后看来,我同意,为此使用管理器不是一个好主意,因为虽然这可能有效,但它也会混淆输入并在将来产生意想不到的结果。
因此我选择了 Mixin 解决方案并实现了以下内容:
# settings.py
NONE_STRING = 'None'
# views.py
from django.conf import settings
class ReplaceNoneMixin(object):
"""Mixin to deal with URL Parameters that represent None. This mixin takes the name of a url parameter and assumes
that if it represents None that it has the value of settings.NONE_STRING. If the parameter is encountered with that
value, the value is changed from NONE_STRING to None."""
kwargs: dict
noneable_parameters_keys: list
def setup(self, request, *args, **kwargs):
"""Changes the value of all url parameters specified in noneable_parameters_keys list. If the parameter has the
settings.NONE_STRING value in kwargs then the value is set to None."""
super().setup(request, *args, **kwargs)
if isinstance(self.noneable_parameters_keys, str):
self.noneable_parameters_keys = [self.noneable_parameters_keys]
for parameter in self.noneable_parameters_keys:
if parameter in self.kwargs and self.kwargs[parameter] == settings.NONE_STRING:
self.kwargs[parameter] = None