2

我将一个项目放在一起,试图尽可能多地利用开源库来最大限度地减少在完成任务时涉及的“打字”。我正在使用django,香脆形式和引导框架。Django处理表单的方式?

我已经编写了用于处理一个实体(添加/编辑/删除)的代码,并且觉得我必须做错事,因为似乎涉及太多的代码 - 有超过20个不同的项目将被管理漂亮同样的方式,所以我想我会向社区征求我可以修复的错误,然后再着手完成剩下的代码。

所以我有一个模型:

class Link(models.Model): 
    name = models.CharField(max_length=255, blank=False, null=False, verbose_name=_(u'Название')) 
    url = models.URLField(blank=False, null=False, verbose_name=_(u'Ссылка')) 
    project = models.ForeignKey('Project') 
    display_on_main_project_page = models.BooleanField(default=False, verbose_name=_(u'Показывать ссылку на главной странице проекта')) 

    class Meta: 
     app_label = 'core' 
     verbose_name = _(u'Ссылка') 
     verbose_name_plural = _(u'Ссылки') 

    def __unicode__(self): 
     return self.name 

和与添加到其作为每手动松脆比特的形式:

class ProjectLinkForm(forms.Form): 
    id = forms.IntegerField(required=False, widget=forms.HiddenInput()) 
    project = forms.ModelChoiceField(queryset=Project.objects.all(),required=True, widget=forms.HiddenInput()) 
    name = forms.CharField(required=True, label=_(u'Имя ссылки')) 
    url = forms.URLField(required=True, label=_(u'URL ссылки')) 
    display_on_main_project_page = forms.BooleanField(label=_(u'Показывать на главной странице проекта'), required=False) 

    class Meta: 
     model = Link 
     fields = ('project','name','url','display_on_main_project_page') 

    def __init__(self, *args, **kwargs): 
     self.helper = FormHelper() 
     self.helper.form_class = 'horizontal-form' 
     self.helper.form_action = '' 
     self.helper.form_id = 'link_form' 
     self.helper.layout = Layout(
      Field('name',css_class='input-block-level'), 
      Field('url', css_class='input-block-level') 
     ) 
     super(ProjectLinkForm, self).__init__(*args, **kwargs) 

和URL配置为服务于以下动作:

url(r'^forms/link/add/(?P<project_id>\d+)/$','core.views.forms.link.add'), 
url(r'^forms/link/edit/(?P<link_id>\d+)/$','core.views.forms.link.edit'), 
url(r'^forms/link/delete/(?P<link_id>\d+)/$','core.views.forms.link.delete'), 

和意见(以添加为例):

@login_required 
def add(request, project_id): 
    if request.method == 'GET': 
     form = ProjectLinkForm(initial={'project':project_id}) 
     form.action = 'add' 
     form.submit_url = request.path 
     return render_to_response('core/forms/project_link.html',{'form':form,'links_form_title':_(u'Добавить ссылку')},context_instance=RequestContext(request)) 
    elif request.method == 'POST': 
     form = ProjectLinkForm(request.POST) 
     form.action = 'add' 
     form.submit_url = request.path 
     if form.is_valid(): 
      try: 
       new_link = Link() 
       new_link.name = form.cleaned_data['name'] 
       new_link.url = form.cleaned_data['url'] 
       new_link.project = form.cleaned_data['project'] 
       new_link.display_on_main_project_page = form.cleaned_data['display_on_main_project_page'] 
       new_link.save() 
       return HttpResponse(status=http_statuses.SAVED, content="saved") 
      except Exception as e: 
       return HttpResponse(status=http_statuses.SERVER_ERROR, content=e.message) 
     else: 
      return render_to_response('core/forms/project_link.html',{'form':form,'links_form_title':_(u'Добавить ссылку')},context_instance=RequestContext(request)) 

    #not POST or GET 
    else: 
     return HttpResponse(status=http_statuses.METHOD_NOT_ALLOWED,content=_(u'Недопустимый тип запроса')) 

有用于所有操作一个模板:

{% load crispy_forms_tags %} 
<div class="modal-header"> 
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> 
    <h3>{{ links_form_title|default:"Ссылки на проект" }}</h3> 
</div> 
<div class="modal-body"> 
    <p> 
     {% if form.action == "delete" %} 
      Уверены, что хотите удалить ссылку "{{ form.name.value }}"? Одно неосторожное движенье мышкой и все - не вернешь! 
      <div class="hide"> 
       {% crispy form %} 
      </div> 
     {% else %} 
      {% crispy form %} 
     {% endif %} 
    </p> 
</div> 
<div class="modal-footer"> 
    {% if form.action == "delete" %} 
     <a href="#" class="btn btn-danger" id="" onclick='SubmitModalForm($("#link_form"),"{{ form.submit_url }}")'>Удалить</a> 
    {% else %} 
    <a href="#" class="btn btn-primary" id="" onclick='SubmitModalForm($("#link_form"),"{{ form.submit_url }}")'>Сохранить</a> 
    {% endif %} 
</div> 

还有一点点的JavaScript代码来处理所有形式的弹出窗口:

function LoadModalForm(url){ 
     $("#dynamic_project_forms").html("").load(url).modal(); 
    } 

    function SubmitModalForm(the_form,submit_url){ 
     $.ajax({ 
      type:'POST', 
      url:submit_url, 
      data: $(the_form).serialize(), 
      complete: function(e, xhr, settings){ 
       if(e.status == 201){//HTTP.STATUS.SAVED 
        $("#dynamic_project_forms").modal('hide'); 
        $(activeTab.hash).load('/api/get_project_tab/'+activeTab.hash.replace('#','')+'/{{ project.id }}/'); 
       } 
       else{ 
        $("#dynamic_project_forms").html(e.responseText); 
        alert('loaded fucking response'); 
       } 
      } 
     }); 
    } 

它的工作,但有这么多打字 - 我想我错过了一些东西,或者只是没有找到正确的东西 - 请建议正确的方法。

回答

5

您可以轻松摆脱一半的代码。

您错过了modelformsgeneric views

你可能只是有这样的URL:

from django.views import generic 


urlpatterns = patterns('test_app.views', 
    url(r'link/create/$', generic.CreateView.as_view(model=Link), 
     name='link_create'), 
    url(r'link/(?P<pk>\d+)/update/$', generic.UpdateView.as_view(model=Link), 
     name='link_update'), 
    url(r'link/(?P<pk>\d+)/delete/$', generic.DeleteView.as_view(model=Link), 
     name='link_delete'), 

    # bonus: 
    url(r'link/$', generic.ListView.as_view(model=Link), name='link_list'), 
    url(r'link/(?P<pk>\d+)/$', generic.DetailView.as_view(model=Link), 
     name='link_detail'), 
) 

但它不支持AJAX就像你在默认情况下 - 但是你可以扩展CreateViewUpdateView和​​,做来支持您的JavaScript所需要的小覆盖。例如,我有这样一个项目:

class AjaxDeleteView(generic.DeleteView): 
    # now that I think of it, this could just be a DetailView ... oh well 
    http_method_names = ['post'] 

    def post(self, *args, **kwargs): 
     self.get_object().delete() 
     return http.HttpResponse('', status=204) 


class AjaxFormMixin(object): 
    def form_valid(self, form): 
     if form.instance.pk: 
      status = 204 
     else: 
      status = 201 

     self.object = form.save() 
     return http.HttpResponse(self.object.pk, status=status) 

延伸Django的通用视图的一些其他的例子:

class PkUrlKwarg(SingleObjectMixin): 
    """ 
    Take the pk from request.GET and sets it to kwargs, useful to avoid 
    reversing urls from javascript 
    """ 
    def get_object(self, queryset=None): 
     self.kwargs[self.pk_url_kwarg] = self.request.REQUEST['pk'] 
     return super(PkUrlKwarg, self).get_object(queryset) 


class WidgetFormMixin(object): 
    def get_form(self, form_class): 
     # [snip] ok there's quite a lot (11 SLOCs) going on here in my case, since Widget* views are 
     # supposed to deal with any subclass of Widget 
     return self.object.configuration_form_instance(self.request) 

    def get_template_names(self): 
     widget_name = self.object.__class__.__name__ 

     return [ 
      'form_designer/widget_forms/%s.html' % widget_name, 
      'form_designer/widget_form.html', 
     ] 


class WidgetSecurity(object): 
    """ 
    Return a queryset of Widget that have a tab in a form which author is 
    request.user. For security. 
    """ 
    def get_queryset(self): 
     return Widget.objects.filter(tab__form__author=self.request.user) 

Finnaly,我的CRUD的意见,有相当多的非默认或多或少复杂逻辑,但仍与代码非常几行:

class WidgetCreateView(WidgetFormMixin, AjaxFormMixin, generic.CreateView): 
    form_class = WidgetForm # overridden by WidgetFormMixin.get_form, but make django happy 


class WidgetUpdateView(PkUrlKwarg, WidgetSecurity, WidgetFormMixin, AjaxFormMixin, generic.UpdateView): 
    form_class = WidgetForm # overridden by WidgetFormMixin.get_form 


class WidgetDeleteView(PkUrlKwarg, WidgetSecurity, AjaxDeleteView): 
    pass 

这可能不是最好的答案,但它应该明确地把你在正确的轨道上,并激励你。

另请注意,您还应该在模型中定义get_absolute_url()

为了记录在案,网址:

url(r'widget/create/$', 
    login_required(WidgetCreateView.as_view()), 
    name='form_designer_widget_create'), 
# the following views accept 'pk' as URL/GET argument 
# this avoids reversing urls from javascript at the cost of a 4-liner mixin 
url(r'widget/update/$', 
    login_required(WidgetUpdateView.as_view()), 
    name='form_designer_widget_update'), 
url(r'widget/delete/$', 
    login_required(WidgetDeleteView.as_view()), 
    name='form_designer_widget_delete'),