I’m putting a project together and attempting to utilize as much of open source libraries as possible to minimize the ‘typing’ involved in getting things done. I am using django, crispy-forms and bootstrap framework.
I’ve written code for handling one entity (add/edit/delete) and feeling that I must be doing something wrong because there seems to be too much code involved – there are over 20 different items that will be managed pretty much the same way and so I thought I’d ask the community for mistakes I can fix before I jump into getting the rest of code done.
So I have a model:
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
and a form with crispy bits added to it as per the manual:
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)
and urls configured to serve the actions:
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'),
and the views (taking add as an example):
@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'Недопустимый тип запроса'))
there is one template used for all actions:
{% load crispy_forms_tags %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</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>
and a bit of JavaScript code to handle all the popups with forms:
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');
}
}
});
}
it does work but there is so much typing – I guess I’m missing something or just not getting something right – please suggest on proper approach.
You can get rid of half of the code easily.
You missed modelforms and generic views.
You could just have such urls:
But it doesn’t support AJAX like you by default – however you could extend
CreateViewUpdateViewandDeleteViewand do the little overrides needed to support your javascript. For example, I have this in a project:Some other examples of extending Django generic views:
Finnaly, my CRUD views, have quite a lot non default and more or less complex logic, but still, with very few lines of code:
This may not be the best answer, but it should definitively put you on the right track, and inspire you.
Also note that you should also define get_absolute_url() in your model.
For the record, urls: