2

私はプロジェクトをまとめて、物事を成し遂げるために必要な「タイピング」を最小限に抑えるために、できるだけ多くのオープンソースライブラリを利用しようとしています。私はdjango、crispy-forms、bootstrapフレームワークを使用しています。

1つのエンティティ(追加/編集/削除)を処理するためのコードを作成しましたが、コードが多すぎるように見えるため、何か間違ったことをしているに違いないと感じています-ほぼ同じ方法で管理される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'),

およびビュー(例としてaddを取り上げます):

@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'Недопустимый тип запроса'))

すべてのアクションに使用される1つのテンプレートがあります。

{% 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');
                }
            }
        });
    }

それは機能しますが、タイピングがたくさんあります-私は何かが欠けているか、何かが正しくないだけだと思います-適切なアプローチを提案してください。

4

1 に答える 1

5

コードの半分を簡単に取り除くことができます。

モデルフォーム一般的なビューを見逃しました。

あなたはそのような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をサポートしていません。ただし、JavaScriptをサポートするために必要な少しのオーバーライドを拡張CreateView UpdateViewして実行することはできます。DeleteViewたとえば、私はこれをプロジェクトに持っています:

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)

最後に、私の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:

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'),
于 2012-11-21T23:28:11.537 に答える