django class-based views (slovenian)
TRANSCRIPT
Class-based views
Luka Zakrajšek@bancek
Django Meet Ljubljana,22. maj 2012
Podatkovna baza
• baze ne potrebujemo, ker do vseh podatkov dostopamo preko REST-a
• radi bi uporabljali Session in Message framework
...
AUTHENTICATION_BACKENDS = ( 'federweb.auth.models.RestEngineBackend',)
INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.contenttypes', ...)
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
settings.py
Avtentikacija
• uporabimo Django Auth framework
• potrebujemo lastni Authentication backend
class RestEngineBackend(object): supports_object_permissions = False supports_anonymous_user = True def authenticate(self, username=None, password=None): users = federation.users.full() for user in users: if user.username == username and user.check_password(password): return user logger.error('Authentication failed for username %s' % username)
def get_user(self, user_id): return federation.users.get(user_id) or None
backend = RestEngineBackend()
user_logged_in.disconnect(update_last_login)
Authentication backend
class AppNodeMixin(object): def __init__(self, *args, **kwargs): super(AppNodeMixin, self).__init__(*args, **kwargs) self.app_name = getattr(self, 'app_name', None) self.node_name = getattr(self, 'node_name', None) self.action_name = getattr(self, 'action_name', None)
def get_template_names(self): if self.template_name: return [self.template_name] action_name = self.action_name or self.node_name template = '%s/%s/%s.html' % (self.app_name, self.node_name, action_name) return [template] def get_context_data(self, **kwargs): ctx = super(AppNodeMixin, self).get_context_data(**kwargs) ctx['request'] = self.request return ctx
App module mixin
def profile_url(user): url_map = { 'FederationCoordinator': reverse('federation'), 'CloudAdministrator': reverse('provider'), 'FederationUser': reverse('user') } roles = user.roles.all() for role in roles: if role.name in url_map: return url_map[role.name] logger.error('Unknown roles for user "%s"' % user)
def home(request): if request.user.is_authenticated(): return HttpResponseRedirect(profile_url(request.user)) else: return redirect_to_login(request.path)
Role router
class RoleRequiredMixin(object): user_role = None @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): user = request.user if self.user_role: if not user or not user.is_authenticated(): raise Http404 roles = user.roles.all() for role in roles: if role.name == self.user_role: return super(RoleRequiredMixin, self).dispatch( request, *args, **kwargs) raise Http404
Role required mixin
class RestMixin(object): model = None def get_context_data(self, **kwargs): ctx = super(RestMixin, self).get_context_data(**kwargs) ctx['model'] = self.model return ctx
REST mixin
class RestDetailMixin(object): model = None action_name = 'detail'
def get_object(self, id): obj = self.model.get(id) if not obj: raise Http404('Object not found') return obj @cached_property def obj(self): return self.get_object(self.kwargs['id']) def get_context_data(self, **kwargs): ct = super(RestDetailMixin, self).get_context_data(**kwargs) ct['object'] = self.obj return ct
REST detail mixin
class RestDetailView(RestDetailMixin, TemplateView): pass
REST detail view
class RestRemoveView(RedirectView): model = None message = None permanent = False def get(self, request, id, *args, **kwargs): obj = self.model.get(id) if not obj.delete(): raise Http404 if self.message: messages.success(self.request, self.message) return super(RestRemoveView, self).get(request, *args, **kwargs)
REST remove object view
urlpatterns = patterns('', url(r'^$', Dashboard.as_view(), name='federation'), url(r'^/providers$', ProvidersList.as_view(), name='federation_providers'), url(r'^/providers/create$', ProvidersCreate.as_view(), name='federation_providers_create'), url(r'^/providers/(?P<id>\d+)/edit$', ProvidersEdit.as_view(), name='federation_providers_edit'), url(r'^/providers/(?P<id>\d+)/remove$', ProvidersRemove.as_view(), name='federation_providers_remove'), ...)
urls.py
class AppMixin(RoleRequiredMixin): app_name = 'federation' user_role = 'FederationCoordinator'
class Dashboard(AppMixin, AppNodeMixin, TemplateView): node_name = 'dashboard' # requires role: "FederationCoordinator" # renders template: "federation/dashboard/dashboard.html"
App views
{% extends 'base.html' %}
{% load common ui %}
{% block head_title %}Federation{% endblock %}
{% block nav %} <li class="{% block nav_dashboard %}{% endblock %}"> <a href="{% url federation %}">Dashboard</a> </li> <li class="{% block nav_users %}{% endblock %}"> <a href="{% url federation_users %}">Users</a> </li> ...{% endblock %}
{% block breadcrumbs %} {% url federation as url %}{% breadcrumb 'Home' url %}{{ block.super }}{% endblock %}
App base template
class ProvidersMixin(AppMixin, AppNodeMixin): node_name = 'providers' model = federation.providers # requires role "FederationCoordinator"
class ProvidersList(ProvidersMixin, RestMixin, TemplateView): pass # renders "federation/providers/providers.html"
class ProvidersEdit(ProvidersMixin, RestDetailMixin, FormView): action_name = 'edit' form_class = ProviderForm success_url = reverse_lazy('federation_providers') ... # renders "federation/providers/edit.html"
class ProvidersRemove(ProvidersMixin, RestRemoveView): message = u'Provider was successfully removed.' url = reverse_lazy('federation_providers') # redirects to "/federation/providers"
Module views
{% extends 'federation/base.html' %}
{% load common ui %}
{% block head_title %}Providers{% endblock %}
{% block nav_providers %}active{% endblock %}
{% block subnav %} <li class="{% block subnav_providers %}{% endblock %}"> <a href="{% url federation_providers %}">Providers</a> </li> <li class="{% block subnav_providers_create %}{% endblock %}"> <a href="{% url federation_providers_create %}">Create Provider</a> </li>{% endblock %}
{% block breadcrumbs %} {% url federation_providers as url %} {% breadcrumb 'Providers' url %}{{ block.super }}{% endblock %}
Module base template
{% extends 'federation/base.html' %}
{% load common ui %}
{% block nav_dashboard %}active{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %} <h2>Hey, {{ request.user.firstName}}</h2>{% endblock %}
Module dashboard
{% extends 'federation/providers/base.html' %}
{% load common ui %}
{% block subnav_providers %}active{% endblock %}
{% block title %} <h2>Providers</h2>{% endblock %}
{% block content %} {% for object in model.full %} <p> {{ object.name }} <a href="{% url federation_providers_edit object.id %}">Edit</a> </p> {% endfor %}{% endblock %}
Objects list
{% extends 'federation/providers/base.html' %}...{% block breadcrumbs %} {% url federation_providers_edit object.id as url %} {% breadcrumb 'Edit provider' url %}{{ block.super }}{% endblock %}
{% block title %}<h2>Edit Provider</h2>{% endblock title %}
{% block content %} <form action="" method="post" class="form-horizontal"> <fieldset> {% csrf_token %} {% form_errors form %} {% form_field form.name 'Name' %} {% form_field form.provider_uri 'Provider URI' %} <button class="btn btn-primary" type="submit">Save</button> </fieldset> </form>{% endblock %}
Object edit
Izvorna koda projekta
• http://websvn.ow2.org/listing.php?repname=contrail&path=%2Ftrunk%2Ffederation%2Ffederation-web%2Fsrc%2Ffederweb%2F
Vprašanja?
• Hvala za pozornost!