我想为我的应用中的每个模型添加一个通用的创建表单。在反复重复相同的几行之后,仅改变模型名称,DRY原则向我发出呼叫。我想出了以下方法,为我的应用程序中的每个模型动态添加表单,视图和路径。
forms.py
from django import forms
from . import models
import inspect
from django.db.models.base import ModelBase
# Add ModelForm for each model
for name, obj in inspect.getmembers(models):
if inspect.isclass(obj) and isinstance(obj, ModelBase):
vars()[name + "ModelForm"] = forms.modelform_factory(obj, exclude=())
views.py
from . import forms
from . import models
import inspect
from django.db.models.base import ModelBase
def form_factory(request, form_type, form_template, redirect_url='index', save=True):
if request.method == "POST":
form = form_type(request.POST)
if form.is_valid():
if save:
form.save()
return HttpResponseRedirect(reverse(redirect_url))
else:
form = form_type()
return render(request, form_template, {'form': form})
# Add view for each model
for name, obj in inspect.getmembers(models):
if inspect.isclass(obj) and isinstance(obj, ModelBase):
form = getattr(forms, name + "ModelForm")
func = lambda request: form_factory(request, form, 'core/create.html')
name = 'create_' + name.lower()
vars()[name] = func
urls.py
from django.urls import path
from . import views
from . import models
import inspect
from django.db.models.base import ModelBase
existing_urls = []
for urlpattern in urlpatterns:
existing_urls.append(urlpattern.pattern._route)
# Add url for each model
for name, obj in inspect.getmembers(models):
if inspect.isclass(obj) and isinstance(obj, ModelBase):
name = name.lower()
url = name + '/new'
if url in existing_urls:
continue
view = getattr(views, 'create_' + name)
url_name = 'create-' + name
urlpatterns.append(path(url, view, name=url_name))
这是个坏主意吗?这感觉不对,但我想不出任何具体的理由不这样做。
答案 0 :(得分:1)
我无法真正看到这种复杂程度的重点。
form_factory
与具体的通用视图一样,它接受参数并直接从URL调用。然后可以简化URLconf以迭代模型,并为每个通过这些参与者的模板添加模式。
然后,您可以使用实际的通用视图进一步简化操作。这些可以直接在URLconf中实例化,而无需在views.py中定义子类。而且,CreateView能够自己构建表单,而无需在forms.py中进行定义。所以你可以摆脱这两个文件。
最后一个简化是使用实际的API来获取模型类:django.apps.apps.get_models()
。所以你需要的只是:
from django.views.generic.edit import CreateView
from django.apps import apps
for model in apps.get_models():
urlpatterns += path(
'{}/new'.format(model._meta.model_name),
CreateView.as_view(
model=model,
template_name='core/create.html',
fields='__all__',
success_url=reverse_lazy('index')
),
name='create-{}'.format(model._meta.model_name)
)
答案 1 :(得分:0)
from django.views.generic.edit import CreateView
from django.apps import apps
for model in apps.get_app_config('appname').get_models():
route = path(
'{}/new'.format(model._meta.model_name),
CreateView.as_view(
model=model,
template_name='core/create.html',
fields='__all__',
success_url=reverse_lazy('index')
),
name='create-{}'.format(model._meta.model_name)
)
urlpatterns.append(route)
添加get_app_config('appname')
可确保只有在所需应用中定义的模型才能获得为其创建的视图。如果没有get_app_config,所有模型都会为它们创建视图,包括django定义的不应该有视图的内容。
使用urlpatterns.append(route)
是附加到python列表的正确方法。上一个答案导致错误。
我建议编辑,但它被拒绝了。 =(