Skip to content

Instantly share code, notes, and snippets.

@auvipy
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save auvipy/1da0d96f826bd8da4d47 to your computer and use it in GitHub Desktop.
Save auvipy/1da0d96f826bd8da4d47 to your computer and use it in GitHub Desktop.
GSoC 2015 Project Proposal: Django Best Practices Updates + FormSet Improvement with formset derived generic views

#Background

Over the years, as Django has evolved, the idea of what constitutes "best practice" has also evolved. However, some parts of Django haven't kept up with those best practices. For example, contrib apps do not use class based views.

In short, Django has been bad at eating it's own dogfood. The contents of contrib should be audited and updated to make sure it meets current best practices.

And another part is django FormSet infrastructure which have limitations which needs to overcome to remove some legacy codes and implement best practices easily to some apps like django admin

Why I have choosed this project to improve django:

Django needs some improvement in implementing class based views in some of its apps/modules so that they can be more extensible by the users and remove legacy code base from django itself. Besides introducing some good features from well known 3rd party projects will help django to implement some of the recent best practices more easily.

Challenges of The Project

The main challenges of the project could be maintaining the backward compatibility and logically factor out all the complex codes to be converted in current best practices. Another thing could be the introduction of new API which could break others parts or incompatible with others which need to be addressed carefully.

The Project Plans

The broader plans for this project is analyzing django contrib apps and findout which parts of them need to be updated so that they can overcome their present drawbacks.

Class Based views can be used to remove some legacy codes from some popular contrib apps which will make them more extensible.

So the value in converting some of the function based views to CBVs the key thing is to identify what features need to be factored out. class-based structure allows us to factor out key functionality in a way that subclasses can override or modify that functionality.

In this case contrib.auth and contrib.admin are the main candidates for this CBV converting.

For updating best practices first thing to consider is to convert the functional views of django contrib apps to class based views where possible and necessary. To do so first app to consider is django contrib auth. Then use them to modify the views in admin app related to authentication. Many features of contrib admin app also need to be converted as class based view for extensibility. Additionally convertion of auth will need some changes in admin app to.

Another thing of my plan is introducing some generic FormSet and ModelformSet views in django proper tat will help to make admin app more extensible with the help of class based views conversion easier.

My Projects aim in GOSC 2015 is to achieve this goal.

Contrib auth module

contrib.auth views.py now have function based views and they are

login logout logout_then_login redirect_to_login password_reset password_reset_done password_reset_confirm password_reset_complete password_change password_change_done

and in urls.py url mapping for function based views as follows

urlpatterns = [ url(r'^login/$', views.login, name='login'), url(r'^logout/$', views.logout, name='logout'), url(r'^password_change/$', views.password_change, name='password_change'), url(r'^password_change/done/$', views.password_change_done, name='password_change_done'), url(r'^password_reset/$', views.password_reset, name='password_reset'), url(r'^password_reset/done/$', views.password_reset_done, name='password_reset_done'), url(r'^reset/(?P[0-9A-Za-z_-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', views.password_reset_confirm, name='password_reset_confirm'), url(r'^reset/done/$', views.password_reset_complete, name='password_reset_complete'), ]

and tests for this in tests/auth_tests/test_views.py

the views have to be converted into CBV and also the urls. and this should be done in a backward compatible manner.

as an example password_change_done view is now implemented as below.

@login_required def password_change_done(request, template_name='registration/password_change_done.html', current_app=None, extra_context=None): context = { 'title': _('Password change successful'), } if extra_context is not None: context.update(extra_context)

if current_app is not None:
    request.current_app = current_app

return TemplateResponse(request, template_name, context)

this could be changed to cbv like below

class PasswordChangeDoneView(TemplateView): template_name='registration/password_change_done.html' current_app=None extra_context=None

@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
	return super(Password_Change_Done, self).dispatch(request, *args, **kwargs)
	
def get_context_data(self, **kwargs):
	context = super(Password_Change_Done, self).get_context_data(**kwargs)
	context.update({
	         'title': _('Password change successful'),
	         'current_app': self.current_app,
	})
	if self.extra_context is not None:
	    context.update(self.extra_context)
	return context

for backward compatibility the following way can be followed

def password_change_done(request, *args, **kwargs): return PasswordChangeDoneView.as_view(**kwargs)(request, *args, **kwargs)

converting auth views to cbv will help to make this auth view more extensible. and using them in other apps will reduce a lot of code.

AdminSite adopting class based auth views

Admin site need to be audited updated with the contrib auth class based views. Changes applied in contrib auth will need to be implemented in some parts of contrib admin and other apps where needed.

#For admin

It would be good to do a) on principle, b) to make the admin views easier to extend, and c) to potentially find and correct issues in the generic class-based views. Naturally, any solution would need to be backwards-compatible.

dog fooding admin app will be most difficult task. For doing so Spliting admin app main views to base, list, edit etc smaller parts could be a feasible option. And editing the relevent files to work with.

for listing the available models the built in generic views should be enough ListView and DetailView

A base view file where AdminViewMixin and AdminHistoryView could be placed and AdminViewMixin will be inherited by many views in other views.

class AdminViewMixin(object):

def __init__(self, **kwargs):
    super(AdminViewMixin, self).__init__(**kwargs)
    self.model = self.admin_opts.model
    self.model_opts = self.model._meta

def get_queryset(self):
    return self.admin_opts.queryset(self.request)

There will be a edit view where all admin edit actions will be placed

Like

AdminAddView AdminDeleteView AdminUpdateView

AdminDeleteView could be like

class AdminDeleteView(AdminViewMixin, DeleteView):

def dispatch(self, request, *args, **kwargs):
    return super(AdminDeleteView, self).dispatch(request, *args, **kwargs)

def get_object(self, queryset=None):
    object = self.admin_opts.get_object(
        self.request, unquote(self.object_id), queryset=queryset)
    if not self.admin_opts.has_delete_permission(self.request, object):
        raise PermissionDenied
    if object is None:
        raise Http404(
            _('%(name)s object with primary key %(key)r does not exist.') % {
                'name': force_unicode(self.model_opts.verbose_name),
                'key': escape(self.object_id)})
    using = router.db_for_write(self.model)
    # Populate deleted_objects, a data structure of all related objects that
    # will also be deleted.
    (self.deleted_objects, self.perms_needed, self.protected) = get_deleted_objects(
        [object], self.model_opts, self.request.user, self.admin_opts.admin_site, using)
    return object

def post(self, *args, **kwargs):
    self.object = self.get_object()
    # The user has already confirmed the deletion.
    if self.perms_needed:
        raise PermissionDenied
    obj_display = force_unicode(self.object)
    self.admin_opts.log_deletion(self.request, self.object, obj_display)
    self.admin_opts.delete_model(self.request, self.object)

    self.admin_opts.message_user(
        self.request, _('The %(name)s "%(obj)s" was deleted successfully.') % {
            'name': force_unicode(self.model_opts.verbose_name),
            'obj': force_unicode(obj_display)})

    if not self.admin_opts.has_change_permission(self.request, None):
        return HttpResponseRedirect(
            reverse('admin:index', current_app=self.admin_opts.admin_site.name))
    return HttpResponseRedirect(
        reverse('admin:%s_%s_changelist' % (
            self.model_opts.app_label, self.model_opts.module_name),
            current_app=self.admin_opts.admin_site.name))

def get_context_data(self, **kwargs):
    context = super(AdminDeleteView, self).get_context_data(**kwargs)

    object_name = force_unicode(self.model_opts.verbose_name)

    if self.perms_needed or self.protected:
        title = _("Cannot delete %(name)s") % {"name": object_name}
    else:
        title = _("Are you sure?")

    context.update({
        "title": title,
        "object_name": object_name,
        "object": self.object,
        "deleted_objects": self.deleted_objects,
        "perms_lacking": self.perms_needed,
        "protected": self.protected,
        "opts": self.model_opts,
        "app_label": self.model_opts.app_label,
    })

    context.update(self.extra_context or {})
    return context

def get_template_names(self):
    form_template = self.admin_opts.delete_confirmation_template
    if form_template:
        return [form_template]
    else:
        return [
            "admin/%s/%s/delete_confirmation.html" % (self.model_opts.app_label, self.model_opts.object_name.lower()),
            "admin/%s/delete_confirmation.html" % self.model_opts.app_label,
            "admin/delete_confirmation.html"
        ]

In list view the changeList related views could be placed Where AdminChangeListView and changeList class will be placed.

The views of admin/options will be returning the converted the Class Based views.

which are now add_view, delete_view, history_view and changelist_view

options.py have huge amount of codes which could be split out with the cbv and generic formsets. ModelAdmin could be modifeid too.

FormSet Improvment

I am considering philosophy to brought in django proper and django-extra-views for FormSet improvement.

From django-extra-views:

Django's class-based generic views are great, they let you accomplish a large number of web application design patterns in relatively few lines of code. They do have their limits though, and that's what this library of views aims to overcome.

I plan is to introduce some small subset from django-extra-views features to django proper to improve its formset infrastructure. which later can be used in django admin and others apps.

FormSet and ModelFormSet views - The formset equivalents of FormView and ModelFormView. InlineFormSetView - Lets you edit formsets related to a model CreateWithInlinesView and UpdateWithInlinesView - Lets users edit a model and its relations Some generic InlineFormSetViews could be introduced for generic foreign keys

Improvment of formset in a generic manner like extra views will help improvement the django formset infrastructure and use that in admin app.

implementation of FormSet

Django Admin New Features

Django admin 2 shows some good ideas about using class based views, features from extra views and themming support etc which makes it more extensible. I am planning to bring some of those goodies in django admin. The feature I'm willing to add to django is the theme changing capability for admin app. User can change the theme of the admin app if they wish. Following the admin two way or any other better easier solutions for this part.

Timeline and Milestone

My semister finals will be completed by 26th of April. So I am planning to work from may 1/2 and allocate 16 weeks for this project

  1. Community bonding period:

At this period I will discuss with the community more detail about the project if there is any tweak needed or better way of implementing my ideas hope fully this might take 1/2 weeks

1st week

Implementing the auth views to cbv with bc and later converting the tests in class based views tests

2nd week

Converting some of admin auth fatures converted to cbv in django admin app

3rd week

Introducing some small subset of features from django extra views like FormSet as a form of generic views

4th week

implementing more class based views for formset

5th week

analyzing admin app to decide which views need to be converted to cbv and why should they implemented

6th week

working on converting some views to edit view like AdminAddViews

7th week

work on admin listViews and historyView

8th week

working more on different parts of admin app to convert them logically to cbv

9th week

completing analyzing

10th week

adding theme changing ability to django admin following suitable

11th week

12th week

13th week

About Me

I'm Asif Saif Uddin (auvi) Postgraduate Student of Institute of Information Technology, Jahangirnagar University, Savar, Dhaka, Bangladesh. Have been working with python for more then 3 years and with django for two years.

Participated in co developement of a local e-commerce project with django after my graduation and then worked full time django developer for a start up. Have worked with django, django rest framework, mezzanine cms and many python/django tools. I like django's batteries included nature and its huge 3rd party app/tools eco system.

After that I enrolled to masters program in IIT, Jahangirnagar University and I'm Firefox student ambassador and club lead of My campus. I'm a opensource fan and trying to utilize the power of open source software as learning tool.

Now I'm looking to participate on gsoc program under DSF to increase my technical ability and learning better internals about django which will eventually make a better developer.

My long term goal is a to become good contributor and learn through contributing to django core.

Contact Information

Email: auvipy@gmail.com

Github: auvipy

IRC nick: auvipy

TimeZone: UTC+6

https://code.djangoproject.com/ticket/17209 https://code.djangoproject.com/ticket/17208 https://code.djangoproject.com/ticket/16256 https://code.djangoproject.com/ticket/10403

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment