django-CRadmin documentation¶
Getting started¶
Install and configure¶
Configure¶
Add django_cradmin
and crispy_forms
to the
INSTALLED_APPS
setting:
INSTALLED_APPS = (
...
'django_cradmin',
'crispy_forms',
...
)
Add django.core.context_processors.request
and
django_cradmin.context_processors.cradmin
to the
TEMPLATE_CONTEXT_PROCESSORS
setting:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.core.context_processors.tz",
"django.contrib.messages.context_processors.messages",
"django.core.context_processors.request",
"django_cradmin.context_processors.cradmin",
)
Set the CRISPY_TEMPLATE_PACK
setting to bootstrap3
:
CRISPY_TEMPLATE_PACK = 'bootstrap3'
Newbie guide¶
What is CRadmin¶
- CRadmin has rolebased accesscontrol.
- CRadmin has one or more apps, called CrApps.
- CRadmin as one or more files called crinstance, which holds all the CrApps.
CRadmin_instance¶
When implementing CRadmin in a Django project, at least one file called crinstance_<crappname>.py
must be created.
This file holds different information about your CrApps, such as roleclass and method for rolequeryset. A
cradmin_instance
can holde one or more CrApps. It is possible for a
cradmin_instance
file to hold CrApps from different Django Apps.
One advantage with the cradmin_instance
is the possibility to easily include different views. These views may have
different roles.
Minimalistic cradmin_instance
example:
class MyCrAdminInstance(crinstance.BaseCrAdminInstance):
roleclass =
rolefrontpage_appname =
apps = [
('CrAppNameHere, <path_to_crapp_module>.App),
]
def get_rolequeryset(self):
Roleclass¶
An easy way to explain the roleclass
is to use the example. A website holds all the
webpages. A very lightweight translation into Python classes can be the classes Site and Page. The cradmin_instance
is then the Site, setting the roleclass
to Site. You request the roleclass with request.cradmin_role
.
We will come back to the roleclass for Page later in the document.
Rolefrontpage appname¶
This is the app to where the user is redirected after chosing a role. Depending on what you build you may have different
apps to where you want to redirect a user. This is solved by creating more than one cradmin_instance
for the CrApp.
However, you can also redirect a not logged-in user to the INDEX
of your app, which would be the
case if you build a website open for the public to read.
Rolequeryset¶
The super django_cradmin.crinstance.BaseCrAdminInstance
describes more in detail the different methods used by
a cradmin_intance
. However if you use the rolebased accesscontrol, the
django_cradmin.crinstance.BaseCrAdminInstance.get_rolequeryset()
gets the role for the authenticated user.
However, if you don’t need a role like in a detailview for a public webpage, you can use the mixin
django_cradmin.crinstance.NoRoleMixin
. There is also a mixin for not requiring a logged-in user in the
django_cradmin.crinstance.NoLoginMixin
. Furthermore the mixin django_cradmin.crinstance.NoLoginMixin
is a shortcut if you neither need a role nor login to see the content held by the cradmin_instance
.
CrApps¶
The module crapps
is at the same level as templates
and tests
in a Django projects. Inside the crapps
directory you will add modules which is the CRadmin app
you want to use in your Django project. The project
structure will then look something like:
django_app
crapps (module)
cradmin_app (module)
__init__ (with urls)
Roles within CrApps¶
If we continue the example with a Website and webpages as mentioned earlier on, you may create a cradmin_app
named
pages_app
. Within this app you will most likely have different kind of views, such as edit, create and delte. Lets
assume the same person should be able to create, edit and delete a webpage. Somewhere, maybe in a mixin, you must
define the role a user has to have to be granted privileges for working with the pages. The attribute roleid_field
must be sat equal to the roleclass defined in the cradmin_instance
file. Finally your views must have a method named
get_queryset_for_role
to set the correct access for the user.
To make it all work, you will have the following classes:
MODELS
class Site(models.Model):
name = models.CharField(max_length=100)
admins = models.ManyToManyField(settings.AUTH_USER_MODEL)
class Page(models.Model):
site = models.ForeginKey(Site)
CRADMIN INSTANCE
class WebCrAdminInstance(crinstance.BaseCrAdminInstance):
id = 'website'
roleclass = Site
rolefrontpage_appname = 'pages'
apps = [
('pages', pages.App),
]
def get_rolequeryset(self):
queryset = Site.objects.all()
if not self.request.user.is_superuser:
queryset = queryset.filter(admins=self.request.user)
return queryset
PAGES APP
class PageCreateUpdateMixin(object):
model = Page
roleid_field = 'site'
def get_queryset_for_role(self):
return Page.objects.filter(site=self.request.cradmin_role)
Views in CrApps¶
There are different types of views within CRadmin. It is important to remember that this is an Django Extension, so if
you don’t know much about views in Django, do start reading the Django Docs. However, the view used for edit a webpage
will be a subclass of the super django_cradmin.viewhelpers.formview.updateview.WithinRoleUpdateView
. This is
a modelbased view, and offcourse there are super classes for create and delete. Ssometimes a modelbased view
just want cut it. In these cases, the django_cradmin.viewhelpers.formview.formview.WithinRoleFormView
may be
your super class. The point is to use the viewhelpers in CRadmin.
Indexview¶
According to django_cradmin.crapp.App.reverse_appindexurl()
it is expected that each CrApp
has a view named
crapp.INDEXVIEW_NAME
. This is the frontpage or homepage for the app.
CRadmin urls¶
We recomend to use the __init__
file within a cradmin__app
to set the urls for each view. Hence the file
contaning you default urls must include the urls to the cradmin_instance
:
url(r'^webpages/', include(WebCrAdminInstance.urls())),
In the __init__
file you will add the django_cradmin.crapp.App
which holds the urls to all different views
within the app. If we continue the website and webpage example, the __init__
file will look something like this for
the pages app:
from django_cradmin import crapp
from django_project.django_app.crapps.pages_app import websiteviews
from django_project.django_app.crapps.pages_app import editviews
class App(crapp.App):
appurls = [
crapp.Url(
r'^$',
websiteviews.IndexView.as_view(),
name=crapp.INDEXVIEW_NAME
),
crapp.Url(
r'^create$',
editviews.PageCreateView.as_view(),
name="create"),
crapp.Url(
r'^edit/(?P<pk>\d+)$',
editviews.PageUpdateView.as_view(),
name="edit"),
crapp.Url(
r'^delete/(?P<pk>\d+)$',
editviews.PageDeleteView.as_view(),
name="delete")
]
Viewhelpers¶
viewhelpers.mixins — View mixin classes¶
-
class
QuerysetForRoleMixin
¶ Bases:
object
Common mixin class for views that query for items limited to items accessible by the current cradmin role.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.mixins.QuerysetForRoleMixin
.-
get_queryset_for_role
()¶ Get a queryset with all objects of
self.model
that the current role can access.
-
get_queryset
()¶ DO NOT override this. Override
get_queryset_for_role()
instead.
-
-
class
CommonCradminViewMixin
¶ Bases:
object
Common mixin class for all cradmin view classes.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.mixins.CommonCradminViewMixin
.Add items to the breadcrumb item list.
If you completely override the
get_breadcrumb_item_list_renderable()
method without calling super (or calling this method explicitly), this method will have no effect.Examples
Simple example:
def add_breadcrumb_list_items(self, breadcrumb_item_list): breadcrumb_item_list.append(url='#', label='Test')
Parameters: breadcrumb_item_list (django_cradmin.crbreadcrumb.BreadcrumbItemList) – The breadcrumb item list to add items to.
Get a breadcrumb item list renderable to use when rendering the template for this view.
By default, this just uses
request.cradmin_app.get_breadcrumb_item_list_renderable()
(seedjango_cradmin.crapp.App.get_breadcrumb_item_list_renderable()
).You will normally only want to override this if you want to customize how breadcrumbs are rendered. If you just need to add items to the breadcrumb item list, override
add_breadcrumb_list_items()
.If you override this, remember that the breadcrumb item list from
request.cradmin_app.get_breadcrumb_item_list_renderable()
can beNone
, so if you use that method you have to remember to handle this.Returns: - A breadcrumb item list renderable object
- or
None
.
Return type: django_cradmin.crbreadcrumb.BreadcrumbItemList
-
add_common_view_mixin_data_to_context
(context)¶ Call this to add common template context variables to the provided
context
.Do not override this in subclasses, but if you create a class that uses this mixin, you must call this in your
get_context_data()
-method.Parameters: context (dict) – A template context.
viewhelpers.generic — Generic views¶
-
class
StandaloneBaseTemplateView
(**kwargs)¶ Bases:
django.views.generic.base.TemplateView
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.javascriptregistry.viewmixin.StandaloneBaseViewMixin
Base template view that you should use instead of
django.views.generic.TemplateView
if you extend thedjango_cradmin/standalone-base.django.html
template.Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.generic.StandaloneBaseTemplateView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
class
WithinRoleTemplateView
(**kwargs)¶ Bases:
django.views.generic.base.TemplateView
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
Base template view that you should use instead of
django.views.generic.TemplateView
if you extend thedjango_cradmin/base.django.html
template.Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.generic.WithinRoleTemplateView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
viewhelpers.detail — Model object detail views¶
-
class
DetailView
(**kwargs)¶ Bases:
django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.viewhelpers.mixins.QuerysetForRoleMixin
,django.views.generic.detail.DetailView
Base detail view for Django cradmin views.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.detail.DetailView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_context_data
(**kwargs)¶ Insert the single object into the context dict.
-
-
class
DetailRoleView
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.detail.DetailView
Extends
DetailView
to streamline creating a detail view for the current role object.Just like
DetailView
, but with the get_object and get_queryset_for_role methods implemented to edit the current role object.Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.detail.DetailRoleView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_object
(queryset=None)¶ Returns the object the view is displaying.
By default this requires self.queryset and a pk or slug argument in the URLconf, but subclasses can override this to return any object.
-
get_queryset_for_role
()¶ Get a queryset with all objects of
self.model
that the current role can access.
-
viewhelpers.formview.formviewmixin — Form view mixins¶
-
class
FormViewMixin
¶ Bases:
object
Mixin class for form views.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.FormViewMixin
.-
listing_viewname
= 'INDEX'¶ Get the view name for the listing page. You can set this, or implement
get_listing_url()
. Defaults todjango_cradmin.crapp.INDEXVIEW_NAME
.
-
get_pagetitle
()¶ Get the page title (the title tag).
-
get_pageheading
()¶ Get the page heading.
Defaults to
get_pagetitle()
.
-
get_form_renderable
()¶ Get a
django_cradmin.renderable.AbstractRenderable
that renders the form.This will typically be a uicontainer — HTML builder with form support tree containing a
django_cradmin.uicontainer.form.Form
, but it can be any AbstractRenderable. Not using adjango_cradmin.uicontainer.form.Form
(or a subclass of it) is fairly complex when it comes to handling error messages and form rendering, so it is generally not recommended.See
django_cradmin.viewhelpers.formview.formview.WithinRoleFormView()
for examples.Returns: The renderable object. Return type: django_cradmin.renderable.AbstractRenderable
-
add_formview_mixin_context_data
(context)¶ Must be used by get_context_data() in subclasses to add the context data required to render the view.
Examples
Adding the required context data:
def get_context_data(self, **kwargs): context = super(MyView, self).get_context_data(**kwargs) self.add_formview_mixin_context_data(context=context) return context
-
get_listing_url
()¶ Get the URL of the listing view.
Defaults to
listing_viewname
. This is used as the success URL by default.
-
get_default_save_success_url
()¶ Get the save success URL.
Defaults to
get_listing_url()
, but ifsuccess_url
is inrequest.GET
, that is used instead.
-
get_success_url
()¶ Defaults to
get_default_save_success_url()
.
-
viewhelpers.formview.formview — Form views¶
-
class
WithinRoleFormView
(**kwargs)¶ Bases:
django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
,django_cradmin.viewhelpers.formview.formviewmixin.FormViewMixin
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django.views.generic.edit.FormView
Form view with the correct context data and sane base template for views where we have a cradmin role.
Uses
django.views.generic.edit.FormView
withdjango_cradmin.viewhelpers.formview.formviewmixin.FormViewMixin
anddjango_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
.Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.WithinRoleFormView
.Examples
Minimalistic example:
from django import forms from django.http import HttpResponseRedirect from django_cradmin import viewhelpers class MyForm(forms.Form): first_name = forms.CharField(max_length=50) last_name = forms.CharField(max_length=50) class MyFormView(viewhelpers.formview.WithinRoleFormView): template_name = 'myapp/myview.django.html' form_class = MyForm def get_form_renderable(self): return uicontainer.layout.AdminuiPageSectionTight( children=[ uicontainer.form.Form( form=self.get_form(), children=[ uicontainer.fieldwrapper.FieldWrapper( fieldname='first_name', # Override field renderable to set autofocus field_renderable=uicontainer.field.Field(autofocus=True) ), uicontainer.fieldwrapper.FieldWrapper('last_name'), uicontainer.button.SubmitPrimary(text='Save') ] ) ] ).bootstrap() def form_valid(self, form): # ... do something with the form ... return HttpResponseRedirect('/some/view')
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_context_data
(**kwargs)¶ Insert the form into the context dict.
-
-
class
StandaloneFormView
(**kwargs)¶ Bases:
django_cradmin.javascriptregistry.viewmixin.StandaloneBaseViewMixin
,django_cradmin.viewhelpers.formview.formviewmixin.FormViewMixin
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django.views.generic.edit.FormView
Form view with the correct context data and sane base template for views where we do not have a cradmin role.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.StandaloneFormView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_context_data
(**kwargs)¶ Insert the form into the context dict.
-
viewhelpers.formview.create_update_view_mixin — Create and update view mixins¶
-
class
CreateUpdateViewMixin
¶ Bases:
django_cradmin.viewhelpers.formview.previewmixin.PreviewMixin
,django_cradmin.viewhelpers.formview.formviewmixin.FormViewMixin
Mixin class for Update and Create views.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.CreateUpdateViewMixin
.-
model
= None¶ The model class.
-
fields
= None¶ List of field names. See
get_model_fields()
andget_field_layout()
.
-
roleid_field
= None¶ The field that should always be set to the current role. Removes the field from the form (see
get_form()
), and instead sets the value directly on the object insave_object()
.
-
get_form
(form_class=None)¶ If you set
roleid_field
, we will remove that field from the form.Note
The
roleid_field
handling also works for GenericForeignKey fields (removes the content type and object pk field from the form).
-
model_verbose_name
¶ Get the verbose name of the model.
-
add_create_update_view_mixin_context_data
(context)¶ Must be called in
get_context_data()
in subclasses to add the required context data.Parameters: context – The template context data.
-
set_automatic_attributes
(obj, form)¶ Called by
save_object()
to set automatic attributes for the object before it is saved.This is where we handle
roleid_field
, but you can override this to set your own automatic attributes. Just remember to callsuper
if you want to keep theroleid_field
magic.Parameters: - obj – The object you are about to save.
- form – The cleaned form.
-
save_object
(form, commit=True)¶ Save the object. You can override this to customize how the form is turned into a saved object.
Make sure you call
super
if you override this (see the docs for the commit parameter). If you do not, you will loose the automatic handling of obj:.roleid_field.Parameters: commit (boolean) – If this is False
, the object is returned unsaved. Very useful when you want to manipulate the object before saving it in a subclass.Returns: The saved object.
-
form_valid
(form)¶ If the form is valid, save the associated model.
-
form_saved
(object)¶ Called after the form has been successfully saved. The
object
is the saved object.Does nothing by default, but you can override it if you need to do something extra post save.
-
get_success_message
(obj)¶ Override this to provide a success message.
The
obj
is the saved object.Used by
add_success_messages()
.
-
add_success_messages
(obj)¶ Called after the form has been saved, and after
form_saved()
has been called.The
obj
is the saved obj.Defaults to add
get_success_message()
as a django messages success message ifget_success_message()
returns anything.You can override this to add multiple messages or to show messages in some other way.
-
serialize_preview
(form)¶ Seralize for preview.
Defaults to serializing the object as JSON using
django.core.serializers
. You can safely override this, but you will also have to overridedeserialize_preview()
.
-
classmethod
deserialize_preview
(serialized)¶ Deseralize a preview serialized with
serialize_preview()
.You must override this and
serialize_preview()
- they work together to send the preview to the preview View.
-
classmethod
get_preview_sessionkey
()¶ Get the session key used for preview. You should not need to override this.
Unlike the default implementation of this method from
django_cradmin.viewhelpers.formbase.PreviewMixin
, we use the model class module and name as the session key. This is simply because we do not want it to matter if you fetch preview data from create or update views for the same model (to simplify implementing preview views).
-
viewhelpers.formview.createview — Create views¶
-
class
CreateViewMixin
¶ Bases:
django_cradmin.viewhelpers.formview.create_update_view_mixin.CreateUpdateViewMixin
Common mixin class for create views.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.CreateViewMixin
.-
editview_appurl_name
= 'edit'¶ The viewname within this app for the edit view. See
get_editurl()
.
-
get_pagetitle
()¶ Get the page title (the title tag).
Defaults to
Create <verbose_name model>
.
-
get_success_message
(obj)¶ Defaults to
"Created "<str(obj)>".
-
get_editurl
(obj)¶ Get the edit URL for
obj
.Defaults to:
self.request.cradmin_app.reverse_appurl(self.editview_appurl_name, args=[obj.pk])
You normally want to use
get_full_editurl()
instead of this method.
-
get_full_editurl
(obj)¶ Get the full edit URL for the provided object.
Unlike
get_editurl()
, this ensures that anysuccess_url
inrequest.GET
is included in the URL.Parameters: obj – A saved model object.
-
-
class
WithinRoleCreateView
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.formview.createview.CreateViewMixin
,django.views.generic.edit.CreateView
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
Create view with the correct context data and sane base template for views where we have a cradmin role.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.WithinRoleCreateView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_context_data
(**kwargs)¶ Insert the form into the context dict.
-
viewhelpers.formview.updateview — Update views¶
-
class
UpdateViewMixin
¶ Bases:
django_cradmin.viewhelpers.formview.create_update_view_mixin.CreateUpdateViewMixin
Common mixin class for update views.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.UpdateViewMixin
.-
get_pagetitle
()¶ Get the page title (the title tag).
Defaults to
Edit <verbose_name model>
.
-
get_success_message
(obj)¶ Override this to provide a success message.
The
obj
is the saved object.Used by
add_success_messages()
.
-
-
class
WithinRoleUpdateView
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.mixins.QuerysetForRoleMixin
,django_cradmin.viewhelpers.formview.updateview.UpdateViewMixin
,django.views.generic.edit.UpdateView
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
Update view with the correct context data and sane base template for views where we have a cradmin role.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.WithinRoleUpdateView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_pagetitle
()¶ Get the page title (the title tag).
Defaults to
Edit <verbose_name model>
.
-
get_success_message
(obj)¶ Override this to provide a success message.
The
obj
is the saved object.Used by
add_success_messages()
.
-
get_context_data
(**kwargs)¶ Insert the form into the context dict.
-
-
class
UpdateRoleView
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.formview.updateview.WithinRoleUpdateView
Extends
UpdateView
to streamline editing the current role object.Just like
UpdateView
, but with the get_object and get_queryset_for_role methods implemented to edit the current role object.Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.UpdateRoleView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_object
(queryset=None)¶ Returns the object the view is displaying.
By default this requires self.queryset and a pk or slug argument in the URLconf, but subclasses can override this to return any object.
-
get_queryset_for_role
()¶ Get a queryset with all objects of
self.model
that the current role can access.
-
-
class
RedirectToCreateIfDoesNotExistMixin
¶ Bases:
object
An update view mixin that redirects to a create view when the object requested does not exist.
You will typically use this for objects with a OneToOne relationship to the current role, but that may not exist. Then you would use something like:
class MyUpdateView(update.UpdateView, update.RedirectToCreateIfDoesNotExistMixin): def get_object(self, queryset=None): return self.get_queryset_for_role().get() def get_queryset_for_role(self): return self.get_model_class().objects.filter( someonetooneattr=self.request.cradmin_role)
And the view will automatically redirect to the create view if the object does not exist.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.RedirectToCreateIfDoesNotExistMixin
.-
createview_appurl_name
= 'create'¶ The viewname within this app for the create view. See
get_createurl()
. Defaults tocreate
.
-
get_createurl
()¶ Get the URL of the create view that you want to redirect to if the requested object does not exist.
Defaults to:
self.request.cradmin_app.reverse_appurl(self.createview_appurl_name)
-
viewhelpers.formview.deleteview — Implements the preview+confirm+delete workflow¶
When to use¶
Use this when you need a view to delete a single item. Your users will get a preview of the item, and the option to confirm the delete or cancel the delete.
Usage¶
The django_cradmin.viewhelpers.formview.WithinRoleDeleteView
is just a subclass of
django.views.generic.DeleteView
, so you use it just like the Django DeleteView.
Very basic example (no security)¶
Lets say you have the following Page
-model in models.py
:
from django.conf import settings
class Page(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
def __str__(self):
return self.title
Then you would create a PageDeleteView and register it in an app with the following code:
from django_cradmin import viewhelpers
from django_cradmin import crapp
class PageDeleteView(viewhelpers.formview.WithinRoleDeleteView):
"""
View used to delete existing pages.
"""
model = Page
class App(crapp.App):
appurls = [
# .. other views
crapp.Url(r'^delete/(?P<pk>\d+)$',
PageDeleteView.as_view(),
name="delete"),
]
Securing the basic example¶
The basic example lets anyone with access to the cradmin delete any page. You normally have multiple roles,
and each role will have access to a subset of objects. Lets add a role class named Site
, and extend our
Page
-model with a foreign-key to that site. Our new models.py
looks like this:
from django.conf import settings
from django.db import models
class Site(models.Model):
name = models.CharField(max_length=100)
admins = models.ManyToManyField(settings.AUTH_USER_MODEL)
class Page(models.Model):
site = models.ForeignKey(Site)
title = models.CharField(max_length=100)
body = models.TextField()
def __str__(self):
return self.title
We make the Site the roleclass on our MyCrAdminInstance
:
from django_cradmin import crinstance
class MyCrAdminInstance(crinstance.BaseCrAdminInstance):
roleclass = Site
# Other stuff documented elsewhere
We only want to allow deleting within the current Site (current role), so we replace model
on the WithinRoleDeleteView with a get_queryset_for_role
-method that limits the pages to
pages within the current site:
class PageDeleteView(viewhelpers.formview.WithinRoleDeleteView):
"""
View used to delete existing pages.
"""
def get_queryset_for_role(self):
return Page.objects.filter(site=self.request.cradmin_role)
API docs¶
-
class
DeleteViewMixin
¶ Bases:
object
Delete view mixin.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.DeleteViewMixin
.-
get_pagetitle
()¶ Get the page title (the title tag).
Defaults to
Delete <verbose_name model>
.
-
get_action_label
()¶ The action we are performing.
Used as the prefix of the page title (see
get_pagetitle()
), and as the default forget_delete_button_label()
.
The label of the delete button.
Defaults to
get_action_label()
.
The label of the cancel button.
Defaults to
get_action_label()
.
-
get_object_preview
()¶ The preview of the object. Used when asking the user if he/she wants to delete the current object.
-
get_confirm_message
()¶ Get the confirm message shown in the focus area of the view.
-
get_success_url
()¶ Get the URL to go to if object was deleted.
Defaults to the INDEX view of the current app.
-
get_success_message
(object_preview)¶ Override this to provide a success message.
Used by
add_success_messages()
.
-
add_success_messages
(object_preview)¶ Called after the object has been deleted.
Defaults to add
get_success_message()
as a django messages success message ifget_success_message()
returns anything.You can override this to add multiple messages or to show messages in some other way.
-
-
class
WithinRoleDeleteView
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.mixins.QuerysetForRoleMixin
,django_cradmin.viewhelpers.formview.deleteview.DeleteViewMixin
,django.views.generic.edit.DeleteView
,django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
,django_cradmin.javascriptregistry.viewmixin.WithinRoleViewMixin
Delete view with the correct context data and sane base template for views where we have a cradmin role.
Note
You should import this class with
from django_cradmin import viewhelpers
, and refer to it usingviewhelpers.formview.WithinRoleDeleteView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
get_context_data
(**kwargs)¶ Insert the single object into the context dict.
-
viewhelpers.formview.previewmixin — Preview view mixin¶
Warning
PreviewMixin
is broken, and may be removed or fixed in a future cradmin release.
-
class
PreviewMixin
¶ Bases:
object
Mixin class to provide a preview view for your views.
You must override
serialize_preview()
,deserialize_preview()
andget_preview_url()
, and you must callstore_preview_in_session()
before opening the preview.You must also ensure your template extends
django_cradmin/viewhelpers/formview_base.django.html
.Preview data is added to a Django session using
store_preview_in_session()
, and popped (fetched and removed) from the session in the preview view usingget_preview_data()
. A typical place to callstore_preview_in_session()
is in the form_valid() method of form views. Example:class MyFormView(viewhelpers.formview.PreviewMixin, viewhelpers.formview.WithinRoleFormView): # Ensure this extends django_cradmin/viewhelpers/formview_base.django.html template_name = 'myapp/mytemplate.django.html' # ... other required code for formbase.FormView ... def form_valid(self, form): if self.preview_requested(): self.store_preview_in_session(self.serialize_preview(form)) return self.render_to_response(self.get_context_data(form=form, show_preview=True)) else: # ... save, update, or whatever you do on POST when preview is not requested ... def get_buttons(self): return [ PrimarySubmit('save', _('Save')), # When this button is clicked, self.preview_requested() returns True (see form_valid above). DefaultSubmit(self.submit_preview_name, _('Preview')) ] def serialize_preview(self, form): return json.dumps({ 'title': form.cleaned_data['title'], 'description': form.cleaned_data['description'], }) @classmethod def deserialize_preview(cls, serialized): return json.loads(serialized)
If you have something like MyFormView implemented, a preview view is as simple as this:
class MyPreviewView(View): def get(request): preview_data = MyFormView.get_preview_data() return HttpResponse(...)
How you render your preview data in your view is entirely up to you - a TemplateView that fetches preview data in get_context_data() is ususally more approproate than a View like the example above.
-
submit_preview_name
= 'submit-preview'¶ The name of the submit button used for preview.
-
preview_requested
()¶ Determine if a preview was requested.
Defaults to checking if
submit_preview_name
is inrequest.POST
.
-
serialize_preview
(form)¶ Seralize data for preview.
You must override this and
deserialize_preview()
- they work together to send the preview to the preview View. You can return anything that can be put into a Django session here. We recommend returning a string to ensure your code work with any session backend. JSON encoding is a good choice is most cases.
-
classmethod
deserialize_preview
(serialized)¶ Deseralize a preview serialized with
serialize_preview()
.You must override this and
serialize_preview()
- they work together to send the preview to the preview View.
-
classmethod
get_preview_sessionkey
()¶ Get the session key used for preview. You should not need to override this.
-
classmethod
get_preview_data
(request)¶ Get the preview data.
You should use this in the preview view to get the data for the preview.
You normally do not override this. If you want to manage serialization yourself, see
serialize_preview()
.
-
get_preview_url
()¶ Get the URL of the preview view.
-
add_preview_mixin_context_data
(context)¶ Must be used by get_context_data() in subclasses to add the context data required to render the view.
Examples
Adding the required context data:
def get_context_data(self, **kwargs): context = super(MyView, self).get_context_data(**kwargs) self.add_preview_mixin_context_data(context=context) return context
-
viewhelpers.uimock — Create UI mocks¶
The uimock module is tailored for creating mock views. It is not ment as a full blown prototyping solution, just as an easy way to create interactive mockups.
Getting started¶
Using uimock is farily easy. You can add it to an existing app, but we recommend you create a separate django app for mocks. With a separate django app, the steps are:
Create a new Django app — a new python module in your project with empty
__init__.py
andmodels.py
.Add the app to the
INSTALLED_APPS
setting.Add
urls.py
to your new app with the following content:# Replace ``myproject_mymockapp`` with your django app name, and # you can use another name than ``myfirstmock``, just make sure # you adjust the template directory in the next step. from django.conf.urls import url from django_cradmin import viewhelpers urlpatterns = [ url(r'^myfirstmock/(?P<mockname>.+)?$', viewhelpers.uimock.UiMock.as_view(template_directory='myproject_mymockapp/myfirstmock/'), name='myproject_mymockapp_mocks'), ]
Create the template directory you specified as argument to UiMock.as_view in the previous step, and add an
index.django.html
template with something like this:{% extends "django_cradmin/viewhelpers/uimock/base.django.html" %} {% block content %} <div class="adminui-page-section"> <div class="container container--tight"> <p>Hello uimock world</p> </div> </div> {% endblock content %} {% block notes %} <p>Some notes here!</p> {% endblock notes %}
Add the urls to your mock app to your project urls:
urlpatterns = [ # ... url(r'^mock/', include('myproject.myproject_mymockapp.urls')), ]
Now you should be able to browse your mock at /mock/myfirstmock/
.
Adding more mock templates¶
To add more mocks, you just add more templates to the same directory where you added
index.django.html
. Their URL will be the same as the index template, with
the name of the template without .django.html
. Make sure your mock templates
extend django_cradmin/viewhelpers/uimock/base.django.html
.
A more full featured example:¶
This is more or less the the same as what we have in django_cradmin/demo/uimock_demo/
.
We demonstrate linking between mocks using
the cradmin_uimock_url()
template
tag, some a bit more complex templates, and show a starting point for mocking of form flows.
index.django.html:
{% extends "django_cradmin/viewhelpers/uimock/base.django.html" %}
{% load cradmin_uimock_tags %}
{% block content %}
<div class="adminui-page-section adminui-page-section--center-md">
<div class="container container--tight">
<p>Hello uimock world</p>
<p>Here is a link to <a href="{% cradmin_uimock_url mockname='ipsum' %}">another mock</a> in the same uimock view</p>
</div>
</div>
{% endblock content %}
{% block notes %}
<h3>Some notes</h3>
<p>Donec sed odio dui. Aenean lacinia bibendum nulla sed consectetur.</p>
<h3>More notes</h3>
<p>
Donec sed odio dui. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh,
ut fermentum massa justo sit amet risus.
</p>
{% endblock notes %}
ipsum.django.html:
{% extends "django_cradmin/viewhelpers/uimock/base.django.html" %}
{% load cradmin_uimock_tags %}
{% block page-cover %}
<header class="adminui-page-cover">
<h1 class="adminui-page-cover__title">Justo Vulputate</h1>
<p class="adminui-page-cover__description">Morbi leo risus, porta ac consectetur ac, vestibulum at eros</p>
</header>
{% endblock page-cover %}
{% block breadcrumbs-below-page-cover %}
<div class="breadcrumb-item-list-wrapper">
<div class="container container--tight">
<nav class="breadcrumb-item-list">
<a class="breadcrumb-item-list__item" href="{% cradmin_uimock_url %}">
Index
</a>
<span class="breadcrumb-item-list__separator"></span>
<span class="breadcrumb-item-list__item breadcrumb-item-list__item--active">
Ipsum mock
</span>
</nav>
</div>
</div>
{% endblock breadcrumbs-below-page-cover %}
{% block content %}
<div class="adminui-page-section">
<div class="container container--tight">
<p>Nullam quis risus eget urna mollis ornare vel eu leo. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Nulla vitae elit libero, a pharetra augue. Nullam quis risus eget urna mollis ornare vel eu leo. Cras mattis consectetur purus sit amet fermentum. Curabitur blandit tempus porttitor.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.</p>
<p>Maecenas faucibus mollis interdum. Donec sed odio dui. Nullam quis risus eget urna mollis ornare vel eu leo. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nullam quis risus eget urna mollis ornare vel eu leo.</p>
<p>Etiam porta sem malesuada magna mollis euismod. Donec ullamcorper nulla non metus auctor fringilla. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit amet fermentum.</p>
</div>
</div>
{% endblock content %}
{% block notes %}
<p>
This uimock example demostrates an a bit more complex example than index.django.html.
We have a page-cover and some breadcrumbs!
</p>
<p>
You may also want to check out <a href="{% cradmin_uimock_url mockname='form' %}">the form mock</a>.
</p>
{% endblock notes %}
form.django.html:
{% extends "django_cradmin/viewhelpers/uimock/base.django.html" %}
{% load cradmin_tags %}
{% block content %}
<div class="adminui-page-section">
<div class="container container--tight">
{% if postdata %}
<h2>Posted data:</h2>
<pre>{{ postdata|cradmin_jsonencode }}</pre>
{% else %}
<form method="POST">
{% csrf_token %}
<label class="label">
Type something
<input type="text" name="something" placeholder="Type something ..." class="input input--outlined" />
</label>
<button class="button button--primary">
Submit form
</button>
</form>
{% endif %}
</div>
</div>
{% endblock content %}
{% block notes %}
<p>Here we mock a form, and when we submit the form, we show the form data a JSON in a PRE tag.</p>
<p>A starting point for mocking form flows.</p>
{% endblock notes %}
Template tags¶
Reverse the URL of a
django_cradmin.viewhelpers.uimock.UiMock
view.Parameters: - mockname (str) – Name of the mock to link to (the name of the template without
.django.html
). If this is not specified, we link toindex.django.html
. - viewname – The name of the mock view (the name specified for the view in urls.py). You do not need to specify this to link to mocks within the same view.
- mockname (str) – Name of the mock to link to (the name of the template without
Mock view API¶
-
class
UiMock
(**kwargs)¶ Bases:
django_cradmin.viewhelpers.generic.StandaloneBaseTemplateView
View for UI mocks.
See viewhelpers.uimock — Create UI mocks for details.
-
template_directory
= None¶ Template directory (prefix for the templates of all the mocks).
Must end with
/
. The directory must contain anindex.django.html
template.You typically send this as an argument to
UiMock.as_view()
(I.E.: you normally do not need to subclass UiMock).See
get_template_names()
for details on how this is used.
-
dispatch
(request, mockname=None, **kwargs)¶ Parameters: - request – The HttpRequest.
- mockname – The name of the requested mock. We expect that a template named
<mockname>.django.html
is available intemplate_directory
. - **kwargs – Other view kwargs (future proofing).
Returns:
-
get_template_names
()¶ Looks up a template in the
template_directory
by the following rules:- If we DO NOT have a
mockname
in view kwargs (seedispatch()
), we return<template_directory>/index.django.html
. - If we have a
mockname
in view kwargs (seedispatch()
), we return<template_directory>/<mockname>.django.html
.
- If we DO NOT have a
-
post
(request, *args, **kwargs)¶ Sets
request.POST
data as a session variable,cradmin_uimock_postdata
, and redirects to the current full URL. In dispatch(), we pop this from session and adds it as thepostdata
template context data.This facilitates mocking simple form flows.
-
Writing tests¶
Writing tests - writing tests for cradmin instances and apps¶
Writing tests for a CrInstance¶
Like this:
from unittest import mock
from django.conf import settings
from django.test import TestCase
from model_mommy import mommy
from exampledjangoapps.exampleapp_dummyname.crinstances.crinstance_question import QuestionCrAdminInstance
class TestQuestionCrAdminInstance(TestCase):
def test_user_that_is_not_superuser_makes_rolequeryset_empty(self):
mommy.make('exampleapp_dummyname.Question')
mockrequest = mock.MagicMock()
mockrequest.user = mommy.make(settings.AUTH_USER_MODEL)
crinstance = QuestionCrAdminInstance(request=mockrequest)
self.assertEqual(0, len(crinstance.get_rolequeryset().all()))
Writing tests for a CrApp¶
Testhelpers — Makes it easier to write tests for your cradmin views¶
Test case mixins¶
-
class
MockRequestResponse
(response, request)¶ Bases:
object
Return type of
TestCaseMixin.mock_request()
,TestCaseMixin.mock_http200_getrequest_htmls()
andTestCaseMixin.mock_postrequest()
.-
response
¶ The HttpResponse object.
-
request
¶ The RequestFactory-generated HttpRequest object.
-
selector
¶ A
htmls.S
object created with the entire content of the response as input. Only available when usingTestCaseMixin.mock_http200_getrequest_htmls()
.
-
-
class
AbstractTestCaseMixin
¶ Bases:
object
Implements the common API for the test case mixins.
-
viewclass
= None¶ The view class - must be set in subclasses unless you override
get_viewclass()
.
-
get_viewclass
()¶ Get the view class. Defaults to
viewclass
-
make_view
()¶ Make view function.
Calls
get_viewclass().as_view()
by default.
-
get_requestfactory_class
()¶ Get the request factory class.
Must be implemented in subclasses.
-
create_default_user_for_mock_request
()¶ Create default user for mock request.
Defaults to returning
mommy.make(settings.AUTH_USER_MODEL)
, which should create a user no matter what user model you are using, unless you do something very complex for users.You can override this if you want to change the default properties of users for all tests in this testcase, but it is normally better to just use the
requestuser
kwarg formock_request()
(or any of the more specializedmock_*request(..)
methods)Must return a user object compatible with
django.http.HttpRequest.user
. This means that the method can also return an AnonymousUser, but if you need this you should useNoLoginTestCaseMixin
(which just overrides this method).
-
make_minimal_request
(method, requestkwargs=None, httpheaders=None)¶ Make a minimal request object.
Parameters: - method (str) – The http method (get, post, …).
- requestkwargs (dict) – Kwargs for the request. Defaults to
{'path': '/'}
, and the path is added unless it is included in the dict. The data you can include in this dict is the same as for the get, post, … methods ondjango.test.Client
(depends on themethod
kwarg). - httpheaders (dict) – Extra http headers needed.
Returns: The created request object.
-
prettyformat_response_content
(response)¶ Must be implemented in subclasses.
-
mock_request
(method, cradmin_role=None, cradmin_app=None, cradmin_instance=None, requestuser=None, messagesmock=None, sessionmock=None, requestattributes=None, httpheaders=None, requestkwargs=None, viewkwargs=None, expected_statuscode=None, htmls_selector=False, verbose=False)¶ Create a mocked request using
make_requestfactory()
andmock.MagicMock
.Parameters: - method – The http method (get, post, …).
- httpheaders (httpheaders) – Forwarded to
make_minimal_request()
. Extra HTTP headers to be added to request.META. - requestkwargs (dict) – Forwarded to
make_minimal_request()
. - cradmin_role – The request.cradmin_role to use. Defaults to mock.MagicMock().
- cradmin_app – The request.cradmin_app to use. Defaults to mock.MagicMock().
- cradmin_instance – The request.cradmin_instance to use. Defaults to mock.MagicMock().
- requestuser – The request.requestuser to use. Defaults to mock.MagicMock().
- sessionmock – The request.session to use. Defaults to mock.MagicMock().
- messagesmock – The request._messages to use. Defaults to mock.MagicMock().
- requestattributes (dict) – Extra attributes to the reques to object.
This is applied before
add_essential_cradmin_attributes_to_request()
, so any of the attributes that method sets can not be in this dics. - viewkwargs (dict) – Kwargs for the view.
- expected_statuscode (int) – Expected status code. If this is
None
, we do not validate the status code. Defaults toNone
. - htmls_selector (boolean) – If this is
True
, we create ahtmls.S()
object for the response content. Should normally not be used without also providingexpected_statuscode
because it can lead to unexpected behavior. Defaults toFalse
. - verbose (boolean) – More verbose exception messages. Useful for debugging,
but should be
False
when you are not debugging. Defaults toFalse
.
-
-
class
TestCaseMixin
¶ Bases:
django_cradmin.cradmin_testhelpers.AbstractTestCaseMixin
A mixin class that makes it easier to write tests for cradmin views.
It mocks all the required attributes of the request object.
Examples
Minimalistic:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_get_render_form(self): mockresponse = self.mock_http200_getrequest_htmls() mockresponse.selector.prettyprint() def test_post(self): mockresponse = self.mock_postrequest(requestkwargs={ 'data': { 'name': 'Jane Doe', 'age': 24 } }) self.assertEqual(mockresponse.response.status_code, 302)
Adding optional HTTP headers if needed:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_post_with_optional_httpheaders(self): self.mock_postrequest( cradmin_instance=cradmin_instance, httpheaders={ 'HTTP_REFERER': 'http://www.http-referent.com' }, requestkwargs={ 'data': { 'name': 'Jane Doe', 'age': 24 } }) self.assertEqual(mockresponse.response.status_code, 302)
Customizing the request.body, if e.g you have created a view that works as an API that handles POST with data. This data will in most cases be JSON. If this is not done through a form, but javascript, the data will end up in request.body, and not request.POST. To have this functionality when testing, simply add the data you want to _body in requestattributes. request._body is the actual attribute, and request.body is a property:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_post_with_optional_httpheaders(self): self.mock_postrequest( cradmin_instance=cradmin_instance, requestattributes={ '_body': b'{'key': 'value'}' }) self.assertEqual(mockresponse.response.status_code, 302)
Views that take arguments:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_get_render_form(self): # The kwargs for mock_postrequest, mock_http200_getrequest_htmls # and mock_getrequest are the same, so only showing one for brevity. mockresponse = self.mock_http200_getrequest_htmls(viewkwargs={'pk': 10})
Views that use a querystring (GET):
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_get_render_form(self): mockresponse = self.mock_http200_getrequest_htmls( requestkwargs={ 'data': { 'orderby': 'name' } } )
Using a real user object:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_post(self): requestuser = mommy.make(settings.AUTH_USER_MODEL) mockresponse = self.mock_http200_getrequest_htmls(requestuser=requestuser)
Mocking Django messages framework messages:
from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_post(self): messagesmock = mock.MagicMock() mockresponse = self.mock_postrequest( requestkwargs={ 'data': { 'name': 'Jane Doe', 'age': 24 }, messagesmock=messagesmock ) messagesmock.add.assert_called_once_with( messages.SUCCESS, 'The data was posted successfully!', '')
Get a bit of extra information useful while debugging failing tests - use
verbose=True
(only for debugging, do not keep/commit verbose=True):from django_cradmin import cradmin_testhelpers class TestMyView(TestCase, cradmin_testhelpers.TestCaseMixin): viewclass = MyView def test_get(self): mockresponse = self.mock_http200_getrequest_htmls( verbose=True )
-
get_requestfactory_class
()¶ Get the request factory class. Defaults to
django.test.RequestFactory
.
-
prettyformat_response_content
(response)¶ Must be implemented in subclasses.
-
-
class
NoLoginTestCaseMixin
¶ Bases:
django_cradmin.cradmin_testhelpers.TestCaseMixin
Use this instead of
TestCaseMixin
if you do not require an authenticated user in your view.-
create_default_user_for_mock_request
()¶ Overridden to return a
django.contrib.auth.models.AnonymousUser
object instead of creating a user.
-
-
class
RestFrameworkApiTestCaseMixin
¶ Bases:
django_cradmin.cradmin_testhelpers.TestCaseMixin
Django-rest-framework API test case mixin.
Works just like
TestCaseMixin
, except that it has a different debug output, and no*_htmls
methods.Assumes that the viewclass is a subclass of
rest_framework.views.APIView
.You can also make it work with viewsets by overriding
make_view()
and usereturn ViewSetClass.as_view({...})
there.-
get_requestfactory_class
()¶ Get the request factory class. Defaults to
django.test.RequestFactory
.
-
prettyformat_response_content
(response)¶ Must be implemented in subclasses.
-
mock_request
(**kwargs)¶ Create a mocked request using
make_requestfactory()
andmock.MagicMock
.Parameters: - method – The http method (get, post, …).
- httpheaders (httpheaders) – Forwarded to
make_minimal_request()
. Extra HTTP headers to be added to request.META. - requestkwargs (dict) – Forwarded to
make_minimal_request()
. - cradmin_role – The request.cradmin_role to use. Defaults to mock.MagicMock().
- cradmin_app – The request.cradmin_app to use. Defaults to mock.MagicMock().
- cradmin_instance – The request.cradmin_instance to use. Defaults to mock.MagicMock().
- requestuser – The request.requestuser to use. Defaults to mock.MagicMock().
- sessionmock – The request.session to use. Defaults to mock.MagicMock().
- messagesmock – The request._messages to use. Defaults to mock.MagicMock().
- requestattributes (dict) – Extra attributes to the reques to object.
This is applied before
add_essential_cradmin_attributes_to_request()
, so any of the attributes that method sets can not be in this dics. - viewkwargs (dict) – Kwargs for the view.
- expected_statuscode (int) – Expected status code. If this is
None
, we do not validate the status code. Defaults toNone
. - htmls_selector (boolean) – If this is
True
, we create ahtmls.S()
object for the response content. Should normally not be used without also providingexpected_statuscode
because it can lead to unexpected behavior. Defaults toFalse
. - verbose (boolean) – More verbose exception messages. Useful for debugging,
but should be
False
when you are not debugging. Defaults toFalse
.
-
Utilities¶
Css classes only for automatic tests¶
You should use the cradmin_test_css_class()
template tag to add css classes that are only used in tests.
If you extend django_cradmin.renderable.AbstractRenderableWithCss
, you
can add css classes only for tests by overriding
get_test_css_class_suffixes_list()
.
Modelhelpers¶
sortable — Making objects sortable¶
To make a model sortable, you need the following two additions to your model:
- You need to inherit from
SortableBase
(an abstract model) instead ofdjango.db.Model
, - You need to create a subclass of
SortableItemQuerySet
and attach that subclass as a queryset for your model.
Example:
from django_cradmin.sortable.models import SortableBase
from django_cradmin.sortable.models import SortableQuerySetBase
class MySortableItemQuerySet(SortableQuerySetBase):
parent_attribute = 'container'
class MySortableItem(SortableBase):
objects = MySortableItemQuerySet.as_manager()
container = models.ForeignKey(ItemContainer, blank=False, null=False)
name = models.CharField(...)
The class that inherits SortableBase
gets an attribute sort_index
. If you want the default ordering
for this model to be this attribute, you should add the following meta option on the model:
class Meta:
ordering = ['sort_index']
How to sort¶
Sorting is done by using these methods:
django_cradmin.sortable.models.SortableQuerySetBase.sort_before()
django_cradmin.sortable.models.SortableQuerySetBase.sort_last()
django_cradmin.sortable.models.SortableQuerySetBase.set_newitem_sort_index_to_last()
Example:
# Make a new item and put it last in the list
myitem = MySortableItem(container=somecontainer, name='Some name')
MySortableItem.objects.set_newitem_sort_index_to_last(myitem)
myitem.save()
# Move the given item before the item with id 777
# NOTE: Unlike set_newitem_sort_index_to_last(), sort_before() and sort_last()
# saves the item.
MySortableItem.objects.sort_before(someitem, sort_before_id=777)
# Move the given item last in the list
MySortableItem.objects.sort_last(someitem)
# ... or ...
MySortableItem.objects.sort_before(someitem, sort_before_id=None)
Makin an Admin UI that automatically adds items last in parent¶
Making an Admin UI that automatically adds items last in parent is easy. Just extend
django_cradmin.sortable.admin.SortableModelAdmin
instead of
django.contrib.admin.ModelAdmin
:
from django_cradmin.sortable.admin import SortableModelAdmin
class MySortableItemAdmin(SortableModelAdmin):
pass
admin.site.register(models.MySortableItem, MySortableItemAdmin)
You may also want to show the sort order by default in the admin UI listing, with something like this:
class MySortableItemAdmin(SortableModelAdmin):
ordering = ['container__name', 'sort_index']
API¶
-
class
SortableQuerySetBase
(model=None, query=None, using=None, hints=None)¶ Bases:
django_cradmin.utils.nulls_last_queryset.NullsLastQuerySet
QuerySet for
SortableManagerBase
.You must use this as a base class if you want to create a custom queryset class for models extending
SortableBase
.-
set_newitem_sort_index_to_last
(item, none_values_order_by=None)¶ Sets
item.sort_index
to the sort_index of the last item in the parent + 1. Does not save.ONLY USE THIS FOR NEWLY CREATED ITEMS.
-
sort_before
(item, sort_before_id, none_values_order_by=None)¶ Sort a given item before the item with id sort_before_id, or last if sort_before_id is
None
.Fetches all items in the same container, and makes changes in the ordering. Only the required updates are made.
-
sort_last
(item, none_values_order_by=None)¶ Just a shortcut for:
self.sort_before(item, sort_before_id=None)
-
-
class
SortableBase
(*args, **kwargs)¶ Bases:
django.db.models.base.Model
Used with
SortableQuerySetBase
to make models sortable.-
sort_index
¶ Sort index -
0
or higher.
-
-
class
SortableModelAdmin
(model, admin_site)¶ Bases:
django.contrib.admin.options.ModelAdmin
ModelAdmin that automatically sets the
sort_index
of newly added items last in their parent. It also makessort_index
read-only by default.Used just like
django.contrib.admin.ModelAdmin
.-
make_sort_index_readonly
= False¶ If this is
True
, we make thesort_index
field read-only. Override this to avoid this magic (typically for debugging).
-
save_model
(request, obj, form, change)¶ Overridden to set the sortindex on save if the pk is None.
-
get_readonly_fields
(request, obj=None)¶ Overridden to make the sortindex readonly if
make_sort_index_readonly
isTrue
.
-
Apps that provide utitities for other apps¶
cradmin_generic_token_with_metadata — Secure generic tokens with metadata¶
The purpose of the django_cradmin.apps.cradmin_generic_token_with_metadata
app is to provide
secure and unique tokens with attached metadata. The tokens are suitable for email confirmation
workflows and public share urls.
Each token belongs to a user and an app. Tokens only live for a limited time, and this time can be configured on a per app basis.
Very useful for single-use URLs like password reset, account activation, etc.
How it works¶
Lets say you have an object and want to generate a unique token for that object:
from django_cradmin.apps.cradmin_generic_token_with_metadata.models import GenericTokenWithMetadata
from django.contrib.auth import get_user_model
myuser = get_user_model().get(...) # The user is the object the token is for.
generictoken = GenericTokenWithMetadata.objects.generate(
app='myapp', content_object=myuser,
expiration_datetime=get_expiration_datetime_for_app('myapp'))
# Use generictoken.token
This creates a GenericTokenWithMetadata object with a token
-attribute that
contains a unique token. The app is provided for two reasons:
- Makes it easier to debug/browse the data model because you know what app generated the token.
- Makes it possible to configure different time to live for each app.
- Isolation. Each app has their own “namespace” of tokens.
When you have a token, typically from part of an URL, and want to get the user owning the token, use:
generictoken = GenericTokenWithMetadata.objects.pop(app='myapp', token=token)
# Use generictoken.user and generictoken.metadata
This returns the GenericTokenWithMetadata, and deletes the GenericTokenWithMetadata from the database.
Use case — password reset email¶
Lets say you want to use GenericTokenWithMetadata to generate a password reset email.
First, we want to give the user an URL where they can go to reset the password:
url = 'http://example.com/resetpassword/{}'.format(
GenericTokenWithMetadata.objects.generate(
app='passwordreset',
content_object=self.request.user,
expiration_datetime=get_expiration_datetime_for_app('passwordreset'))
Since we are using Django, we will most likely want the url to be to a view, so this would most likely look more like this:
def start_password_reset_view(request):
url = request.build_absolute_uri(reverse('my-reset-password-accept-view', kwargs={
'token': GenericTokenWithMetadata.objects.generate(
app='passwordreset', content_object=self.request.user,
expiration_datetime=get_expiration_datetime_for_app('passwordreset'))
}
# ... send an email giving the receiver instructions to click the url
In the view that lives at the URL that the user clicks to confirm the password reset request, we do something like the following:
class ResetThePassword(View):
def get(request, token):
try:
token = GenericTokenWithMetadata.objects.get_and_validate(app='passwordreset', token=token)
except GenericTokenWithMetadata.DoesNotExist:
return HttpResponse('Invalid password reset token.')
except GenericTokenExpiredError:
return HttpResponse('Your password reset token has expired.')
else:
# show a password reset form
def post(request, token):
try:
token = GenericTokenWithMetadata.objects.pop(app='passwordreset', token=token)
except GenericTokenWithMetadata.DoesNotExist:
return HttpResponse('Invalid password reset token.')
else:
# reset the password
Configure¶
You can configure the time to live of the generated tokens using the
DJANGO_CRADMIN_SECURE_USER_TOKEN_TIME_TO_LIVE_MINUTES
setting:
DJANGO_CRADMIN_SECURE_USER_TOKEN_TIME_TO_LIVE_MINUTES = {
'default': 1440,
'myapp': 2500
}
It defaults to:
DJANGO_CRADMIN_SECURE_USER_TOKEN_TIME_TO_LIVE_MINUTES = {
'default': 60*24*4
}
Delete expired tokens¶
To delete expired tokens, you can use:
GenericTokenWithMetadata.objects.delete_expired()
or the cradmin_generic_token_with_metadata_delete_expired
management command:
$ python manage.py cradmin_generic_token_with_metadata_delete_expired
Note
You do not need to delete expired tokens very often unless you generate a lot of
tokens. Expired tokens are not available through the GenericTokenWithMetadataBaseManager.pop()
method. So if you use the API as intended, you will never use an expired token.
API¶
-
get_time_to_live_minutes
(app)¶ Get the configured time to live in minutes for tokens for the given
app
.
-
get_expiration_datetime_for_app
(app)¶ Get the expiration datetime of tokens for the given
app
relative tonow
.If the given app is configured to with 60 minutes time to live, this will return a datetime object representing 60 minutes in the future.
-
generate_token
()¶ Generate a token for the
GenericTokenWithMetadata.token
field.Joins an UUID1 (unique uuid) with an UUID4 (random uuid), so the chance of this not beeing unique is very low, and guessing this is very hard.
Returns: A token that is very unlikely to not be unique.
-
exception
GenericTokenExpiredError
¶ Bases:
Exception
Raised by
GenericTokenWithMetadata.get_and_validate()
when the token is found, but has expired.
-
class
GenericTokenWithMetadataQuerySet
(model=None, query=None, using=None, hints=None)¶ Bases:
django.db.models.query.QuerySet
QuerySet for
GenericTokenWithMetadata
.-
unsafe_pop
(app, token)¶ Get the
GenericTokenWithMetadata
matching the given token and app. Removes the GenericTokenWithMetadata from the database, and returns the GenericTokenWithMetadata object.You should normally use
GenericTokenWithMetadataBaseManager.pop()
instead of this.Raises: - GenericTokenWithMetadata.DoesNotExist if no matching token is stored for
- the given app.
-
filter_has_expired
()¶ Return a queryset containing only the expired GenericTokenWithMetadata objects in the current queryset.
-
filter_not_expired
()¶ Return a queryset containing only the un-expired GenericTokenWithMetadata objects in the current queryset.
-
filter_by_content_object
(content_object)¶ Filter by
GenericTokenWithMetadata.content_object
.Examples
Lets say the content_object is a User object, you can find all tokens for that user in the
page_admin_invites
app like this:from django.contrib.auth import get_user_model user = get_user_model() GenericTokenWithMetadata.objects .filter(app='page_admin_invites') .filter_by_content_object(user)
-
filter_usable_by_content_object_in_app
(content_object, app)¶ Filters only non-expired tokens with the given
content_object
andapp
.
-
-
class
GenericTokenWithMetadataBaseManager
¶ Bases:
django.db.models.manager.Manager
Manager for
GenericTokenWithMetadata
.Inherits all methods from
GenericTokenWithMetadataQuerySet
.-
generate
(app, expiration_datetime, content_object, metadata=None)¶ Generate and save a token for the given user and app.
Returns: A GenericTokenWithMetadata
object with a token that is guaranteed to be unique.
-
pop
(app, token)¶ Get the
GenericTokenWithMetadata
matching the given token and app. Removes the GenericTokenWithMetadata from the database, and returns the GenericTokenWithMetadata object.Does not return expired tokens.
Raises: GenericTokenWithMetadata.DoesNotExist
– If no matching token is stored for the given app, or if the token is expired.
-
delete_expired
()¶ Delete all expired tokens.
-
get_and_validate
(app, token)¶ Get the given
token
for the givenapp
.Raises: GenericTokenWithMetadata.DoesNotExist
– If the token does not exist.GenericTokenExpiredError
– If the token has expired.
-
-
class
GenericTokenWithMetadata
(*args, **kwargs)¶ Bases:
django.db.models.base.Model
Provides a secure token with attached metadata suitable for email and sharing workflows like password reset, public share urls, etc.
-
app
¶ The app that generated the token. You should set this to the name of the app the generated the token.
-
token
¶ A unique and random token, set it using
generate_token()
.
-
created_datetime
¶ Datetime when the token was created.
-
expiration_datetime
¶ Datetime when the token expires. This can be None, which means that the token does not expire.
-
single_use
¶ Single use? If this is False, the token can be used an unlimited number of times.
-
metadata_json
¶ JSON encoded metadata
-
content_type
¶ The content-type of the
content_object
. Together withobject_id
this creates a generic foreign key to any Django model.
-
object_id
¶ The object ID of the
content_object
. Together withcontent_type
this creates a generic foreign key to any Django model.
-
content_object
¶ A
django.contrib.contenttypes.fields.GenericForeignKey
to the object this token is for.This generic relationship is used to associate the token with a specific object.
Use cases:
- Password reset: Use the content_object to link to a User object when you create password reset tokens.
- Invites: Use the content_object to link to the object that you are inviting users to. This enables you to filter on the content object to show pending shares.
-
is_expired
()¶ Returns True if
GenericTokenWithMetadata.expiration_datetime
is in the past, and False if it is in the future or now.
-
exception
DoesNotExist
¶ Bases:
django.core.exceptions.ObjectDoesNotExist
-
exception
MultipleObjectsReturned
¶ Bases:
django.core.exceptions.MultipleObjectsReturned
-
metadata
¶ Decode
GenericTokenWithMetadata.metadata_json
and return the result.Return None if metadata_json is empty.
-
Utilities¶
urlutils — Utilities for working with URLs¶
-
create_querydict
(querystringargs, initial_query_string=None, ignore_none_values=True)¶ Parameters: - querystringargs (dict) – The querystring args to add/replace.
- initial_query_string (str) – The initial querystring. Any ovelapping
keys between this and
querystringargs
is overridden by the value inquerystringargs
. - ignore_none_values (bool) – If this is
True
(default), we ignoreNone
values inquerystringargs
.
Returns: The created
django.http.request.QueryDict
.
-
update_querystring
(url, querystringargs, ignore_none_values=True)¶ Update the querystring portion of the given
url
.Parameters: Returns: The updated url.
Examples
Add querystring argument:
from django_cradmin import urlutils urlutils.update_querystring('http://example.com', {'search': 'test'})
Update querystring argument:
urlutils.update_querystring('http://example.com?search=something&page=2', {'search': 'updated'})
renderable — Unified renderable interface¶
When you build decoupled modules where separate items is rendered, you
often need to render several different templates into one output.
One approach is to just use the {% include %}
tag, but that
is not a very object oriented approach. To make this object oriented,
we use the django_cradmin.renderable.AbstractRenderable
class to provide a unified interface inspired by the TemplateView
class in Django.
To provide a renderable object, you simply subclass
django_cradmin.renderable.AbstractRenderable
, specify
a template name, and add methods, attributes and properties to
the class to make them available to the template.
-
join_css_classes_list
(css_classes_list)¶ Join the provided list of css classes into a string.
-
class
AbstractRenderable
¶ Bases:
object
An abstract class that implements an interface for rendering something.
Everything is just helpers for the
render()
method, which renders a template with an object of this class as input.-
template_name
= None¶ The default value for
get_template_names()
.
-
get_template_names
()¶ Get the template name(s) for
render()
.Defaults to
template_name
.Raises: NotImplementedError
– Iftemplate_name
is not set.
-
render
(request=None, extra_context_data=None)¶ Render
get_template_names
with the context returned byget_context_data()
.- Paramteters:
- request (HttpRequest): If this is provided, we forward it to
get_context_data()
, and torender_to_string()
(which is used to render the template).
-
-
class
AbstractRenderableWithCss
¶ Bases:
django_cradmin.renderable.AbstractRenderable
Extends
AbstractRenderable
with a unified API for setting CSS classes.-
get_css_classes_list
()¶ Override this to define css classes for the component.
Must return a list of css classes.
See
get_css_classes_string()
.
-
get_test_css_class_suffixes_list
()¶ List of css class suffixes to include when running automatic tests.
These suffixes are filtered through the
cradmin_test_css_class()
template tag.
-
css_classes
¶ Get css classes.
Joins
get_css_classes_list()
into a string.You should not override this, override
get_css_classes_list()
instead.
-
-
class
AbstractBemRenderable
(bem_block=None, bem_element=None, bem_variant_list=None, extra_css_classes_list=None)¶ Bases:
django_cradmin.renderable.AbstractRenderable
Base class for renderables that uses BEM (http://getbem.com/) for their CSS class naming.
This is an alternative to
AbstractRenderableWithCss
that makes it much more natural to work with BEM.Parameters: - bem_block (str) – Get the BEM block. Can not be supplied if
bem_element
is supplied. - bem_element (str) – Get the BEM element. Can not be supplied if
bem_block
is supplied. - bem_variant_list (list) – Get a list of BEM variants for the block/element.
You do not include the block/element, just the part after
--
. - extra_css_classes_list (list) – List of extra css classes.
-
get_test_css_class_suffixes_list
()¶ List of css class suffixes to include when running automatic tests.
These suffixes are filtered through the
cradmin_test_css_class()
template tag.
-
bem_block_or_element
¶ Returns
get_bem_block()
falling back toget_bem_element()
.
-
get_bem_block
()¶ Get the bem block string.
-
get_bem_element
()¶ Get the bem element string.
-
get_bem_variant_list
()¶ Get a list of BEM variants.
You do not include the block/element, just the part after
--
.
-
get_extra_css_classes_list
()¶ Get a list of extra css classes.
-
css_classes
¶ Get css classes as a string.
You should not override this, override
get_bem_block()
/get_bem_element()
andget_bem_variant_list()
instead.
- bem_block (str) – Get the BEM block. Can not be supplied if
utils.crhumanize — Utilities for humanizing data¶
-
human_readable_filesize
(size_in_bytes)¶ Humanize the given file size in bytes.
Returns a number suffixed with
B
,KB
,MB
,GB
orTB
.Examples
>>> from django_cradmin.utils import crhumanize >>> crhumanize.human_readable_filesize(1) '1B' >>> crhumanize.human_readable_filesize(2344234345) '2.34GB' >>> crhumanize.human_readable_filesize(23442343451234) '23.44TB'
-
dehumanize_readable_filesize
(humanized_size)¶ Does the opposite of
human_readable_filesize()
.Takes a string containing a number suffixed with
B
,KB
,MB
,GB
orTB
, and returns an int with the number of bytes.Examples
>>> from django_cradmin.utils import crhumanize >>> crhumanize.dehumanize_readable_filesize('999B') 999 >>> crhumanize.dehumanize_readable_filesize('2.34GB') 2340000000 >>> crhumanize.dehumanize_readable_filesize('43.312TB') 43312000000000
delay_middleware — Middleware for testing how your app behaves with latency¶
-
class
DelayMiddleware
(get_response)¶ Bases:
object
To use this, you must add the following to your settings:
- Add
django_cradmin.delay_middleware.DelayMiddleware
toMIDDLEWARE_CLASSES
. - Set
DJANGO_CRADMIN_DELAY_MIDDLEWARE_MILLISECONDS
to the number of milliseconds delay you want to add to all requests (I.E.: 2000 for 2 seconds).
- Add
messages_debug_middleware — Debug django messages rendering/styles¶
uicontainer — HTML builder with form support¶
Specifying CSS classes in uicontainer¶
TODO: Explain the various kwargs for styling.
uicontainer and BEM¶
We use BEM syntax for the cradmin CSS, and we have designed the uicontainer library with built in support for working with BEM.
This does not mean that you are forced to use BEM with uicontainer, it simly means that we have some shortcuts/convenience stuff that makes using BEM natural and easy.
More information about BEM at http://getbem.com/.
crbreadcrumb — Generalized breadcrumb rendering¶
Renderables¶
The cradmin.crbreadcrumb
module provides generalized renderables for breadcrumbs. There
renderables can be used to render breadcrumbs anywhere on a page using
the cradmin_render_renderable()
template tag.
Cascading breadcrumbs in cradmin instances, apps and views¶
Having renderables for breadcrumbs is good, but it does not help with the challenge of having to create the full breadcrumb path in each view, and having to refactor that everywhere if we move views around. To solve this problem, we have added support for breadcrumbs in:
django_cradmin.crinstance.BaseCrAdminInstance
django_cradmin.crapp.App
django_cradmin.viewhelpers.mixins.CommonCradminViewMixin
(mixin used by all our base views)
All of these classes have a get_breadcrumb_item_list_renderable()
-method. The
method on crinstance.BaseCrAdminInstance creates a breadcrumb item list renderable object
(a subclass of django_cradmin.crbreadcrumb.BreadcrumbItemList
). The method
on crapp.App just call request.cradmin_instance.get_breadcrumb_item_list_renderable()
to get the breadcrumb item list from the cradmin instance. And, in the same spirit, the
method on viewhelpers.mixins.CommonCradminViewMixin just call
request.cradmin_app.get_breadcrumb_item_list_renderable()
to get the breadcrumb item
list from the cradmin app. Docs for each of these methods here:
django_cradmin.crinstance.BaseCrAdminInstance.get_breadcrumb_item_list_renderable()
django_cradmin.crapp.App.get_breadcrumb_item_list_renderable()
django_cradmin.viewhelpers.mixins.CommonCradminViewMixin.get_breadcrumb_item_list_renderable()
As a convenience, crinstance.BaseCrAdminInstance, crapp.App and viewhelpers.mixins.CommonCradminViewMixin also define
a add_breadcrumb_list_items()
-method which can be used to just add breadcrumb items as long
as the cradmin instance/app actually provide a breadcrumb item list rendereable. You normally
want to override these methods, and they are documented here:
django_cradmin.crinstance.BaseCrAdminInstance.add_breadcrumb_list_items()
django_cradmin.crapp.App.add_breadcrumb_list_items()
django_cradmin.viewhelpers.mixins.CommonCradminViewMixin.add_breadcrumb_list_items()
Note
These methods are named add_breadcrumb_list_items(). This does not mean that you can only add items (it is just the use case in 95% of use cases). Since you have a BreadcrumbItemList, you can add, remove, insert, extend, etc, not just add.
Example¶
A typical and simple example:
class SiteEditView(viewhelpers.formview.UpdateRoleView):
def add_breadcrumb_list_items(self, breadcrumb_item_list):
breadcrumb_item_list.append(label='Edit', active=True)
class SiteOverviewView(viewhelpers.generic.WithinRoleTemplateView):
# No add_breadcrumb_list_items() method because this is the index view for
# the app, so the breadcrumb the app adds takes you to this view.
pass
class SiteDetailsApp(crapp.App):
appurls = [
crapp.Url(r'^$', SiteOverviewView.as_view(), name=crapp.INDEXVIEW_NAME),
crapp.Url(r'^edit$', SiteEditView.as_view(), name='edit'),
]
def add_breadcrumb_list_items(self, breadcrumb_item_list):
breadcrumb_item_list.append(
url=self.reverse_appindexurl(),
label='{} details'.format(
self.request.cradmin_instance.get_titletext_for_role(self.request.cradmin_role))
)
class SiteCradminInstance(crinstance.BaseCrAdminInstance):
rolefrontpage_appname = 'details'
apps = [
('details', SiteDetailsApp)
]
def add_breadcrumb_list_items(self, breadcrumb_item_list):
if self.get_rolequeryset().count() > 1:
# Add breadcrumb back to the roleselect view if we have more than one site
breadcrumb_item_list.append(
url=self.get_instance_frontpage_url(),
label='Sites')
Custom breadcrumb item list renderable:
class MyCustomBreadcrumbItemList(crbreadcrumb.WrappedBreadcrumbItemList):
# We just override the css class in this example, but you can do much more!
def get_bem_block(self):
return 'my-custom-breadcrumb-item-list'
class SiteCradminInstance(crinstance.BaseCrAdminInstance):
# Everything else is just like in SiteCradminInstance above, but
# we add this property
breadcrumb_item_list_renderable_class = MyCustomBreadcrumbItemList
Custom breadcrumb item list in just a single view:
# This works for crapp.App too if you want a custom breadcrumb style for all views in an app!
class SiteEditView(viewhelpers.formview.UpdateRoleView):
def add_breadcrumb_list_items(self, breadcrumb_item_list):
breadcrumb_item_list.append(label='Edit', active=True)
def get_breadcrumb_item_list_renderable(self):
# We get the breadcrumb item list from super() this will include everything from
# the cradmin instance and app, and the item we added in add_breadcrumb_list_items()
# above.
breadcrumb_item_list = super().get_breadcrumb_item_list_renderable()
if breadcrumb_item_list is None:
# Handle that breadcrumbs may have been disabled in the cradmin instance or app.
return None
# Then we create an instance of MyCustomBreadcrumbItemList with a copy
# of the items of the items from super().
return MyCustomBreadcrumbItemList.from_breadcrumb_item_list(breadcrumb_item_list)
Rendering breadcrumbs at a custom location¶
Lets say your design requires you to render the breadcrumb centered above the title in the page-cover on a certain page. You can achieve this fairly easily.
This can be solved in many different ways, but we will go with a fairly easy solution where we:
- Use
BreadcrumbItemList
instead ofWrappedBreadcrumbItemList
to get a plain<nav class="breadcrumb-item-list">
without any wrapper. - Set a custom location for the breadcrumb so it is not rendered at the default location.
- Render the breadcrumb in the
page-cover-content
block.
First, we override get_breadcrumb_item_list_renderable
on the view:
class SiteEditView(viewhelpers.formview.UpdateRoleView):
def add_breadcrumb_list_items(self, breadcrumb_item_list):
breadcrumb_item_list.append(label='Edit', active=True)
def get_breadcrumb_item_list_renderable(self):
# We have an example above that explains how just copying items works in detail.
breadcrumb_item_list = super().get_breadcrumb_item_list_renderable()
if breadcrumb_item_list is None:
return None
breadcrumb_item_list = crbreadcrumb.BreadcrumbItemList.from_breadcrumb_item_list(breadcrumb_item_list)
# We set a custom location to avoid rendering the breadcrumbs
# at the default location
breadcrumb_item_list.set_location('custom')
return breadcrumb_item_list
Next, we override the template:
{% extends "django_cradmin/viewhelpers/formview/within_role_update_view.django.html" %}
{% load cradmin_tags %}
{% block page-cover-content %}
{# Render breadcrumbs here instead #}
<div class="text-center paragraph">
{% cradmin_render_breadcrumb_item_list %}
</div>
{{ block.super }}
{% endblock page-cover-content %}
Note
You can change how the breadcrumbs are rendered for all views in your
site by overring the breadcrumb item list renderable
on your cradmin instances (typically with a common base class or mixin class),
and override the django_cradmin/standalone-base.django.html
template.
Disabling breadcrumbs¶
Disabling breadcrumbs can be done on cradmin instance, app or views. It is
the same for all of them, you just override the get_breadcrumb_item_list_renderable()
method and return None
:
# View
class SiteEditView(viewhelpers.formview.UpdateRoleView):
def get_breadcrumb_item_list_renderable(self):
return None
# App
class SiteDetailsApp(crapp.App):
def get_breadcrumb_item_list_renderable(self):
return None
# Cradmin instance
class SiteCradminInstance(crinstance.BaseCrAdminInstance):
def get_breadcrumb_item_list_renderable(self):
return None
crbreadcrumb module¶
Bases:
django_cradmin.renderable.AbstractBemRenderable
Breadcrumb item renderable.
Parameters: - label (str) – The text/label.
- url (str) – The link URL. If this is None or any other boolean false value, we render the item as a <span> instead of as a <a> element.
- active (bool) – If this is
True
, we add the--active
BEM variant to the element. - label_maxlength (int) – The max length of the label before it is shortened using the
truncatechars
django template filter. You typically do not set this directly, but instead overrideBreadcrumbItemList.get_default_item_label_maxlength()
to get uniformed max lengths for all your breadcrumbs. - active_label_maxlength (int) – The max length of the label if active=True before the label
is shortened using the
truncatechars
django template filter. You typically do not set this directly, but instead overrideBreadcrumbItemList.get_default_active_item_label_maxlength()
to get uniformed max lengths for all your breadcrumbs. - render_link_for_active (bool) – If this is
True
, we render as a link (<a>
) even ifactive
isTrue
. We do, of course, not render as a link if we do not have anurl
. - parent_bem_block – Provided automatically by
BreadcrumbItemList
.
Get the bem element string.
Get a list of BEM variants.
You do not include the block/element, just the part after
--
.
Should we render the element as a link (
<a>
)?
Get HTML element attributes as a dict.
Bases:
django_cradmin.renderable.AbstractBemRenderable
Breadcrumb separator renderable.
Get the bem element string.
Bases:
django_cradmin.renderable.AbstractBemRenderable
List of breadcrumb items.
We provide a lot of helper methods, but if you need more powerful features, such as removal of breadcrumb items, you can just manipulate the
breadcrumb_item_list
attribute directly. All the append*, insert*, etc. methods just add items to this list.list – A plain python list of
django_cradmin.renderable.AbstractRenderable
objects, normallyBreadcrumbItem
objects.
django_cradmin.crinstance.BaseCrAdminInstance – The cradmin instance sent in as an argument to
__init__()
.
Parameters: cradmin_instance (django_cradmin.crinstance.BaseCrAdminInstance) – A cradmin instance. Render location – Above page cover.
Render location – Below page cover.
Get a list of extra css classes.
The location to render the breadcrumb at by default.
Can be overridden on a per view, per app or per cradmin instance basis using
set_location()
.Only relevant if using the
django_cradmin.templatetags.cradmin_tags.cradmin_render_breadcrumb_item_list()
template tag with thelocation
argument. Defaults toLOCATION_BELOW_PAGE_COVER
.
Get the location to render the breadcrumb item list at.
Do not override this - override
get_default_location()
instead.
Set the location to render the breadcrumb item list at.
Can be used in the
get_breadcrumb_item_list_renderable()
method of cradmin instances, apps or views to override the location to render breadcrumbs at.Parameters: location (str) – Location.
Override this to use a custom BEM block for the breadcrumb.
Get a list of BEM variants.
You do not include the block/element, just the part after
--
.
Get the renderable class for breadcrumb item separators.
Defaults to
BreadcrumbSeparator
.
Make a breadcrumb item separator renderable.
Get the renderable class for breadcrumb items.
Defaults to
BreadcrumbItem
.
The default max length of the label of items.
If the label is longer than this, the label is truncated using the
truncatechars
django template filter.If you return None or another boolean false falue from this method, labels are not truncated.
Returns: The max length of item labels, or None
. Defaults to15
.Return type: int
The default max length of the label of active items.
If the label is longer than this, the label is truncated using the
truncatechars
django template filter.If you return None or another boolean false falue from this method, labels are not truncated.
Returns: The max length of item labels, or None
. Defaults to25
.Return type: int
Get kwargs for the
get_item_renderable_class()
.Make sure you call super to get the required kwargs if you override this method.
Parameters: **extra_kwargs – Extra kwargs. Returns: kwargs. Return type: dict
Make an item renderable.
Parameters: **kwargs – Kwargs for the item renderable class. This is forwarded to get_item_renderable_kwargs()
to enable default kwargs for all items in the breadcrumb item list.
Uses
make_item_renderable()
to create a renderable, and appends the created item to the breadcrumb list.
Uses
make_item_renderable()
to create a renderable, and prepends the created item to the breadcrumb list.
Uses
make_item_renderable()
to create a renderable, and inserts the created item in the breadcrumb list.
Insert a renderable object at a specific index in the breadcrumb list.
Parameters: - index (int) – The index to insert at.
- renderable_object – The renderable object (a subclass
of
django_cradmin.renderable.AbstractRenderable
)
Prepend a renderable object to the breadcrumb list.
Parameters: renderable_object – The renderable object (a subclass of django_cradmin.renderable.AbstractRenderable
)
Append a renderable object to the breadcrumb list.
Parameters: renderable_object – The renderable object (a subclass of django_cradmin.renderable.AbstractRenderable
)
Just like
append()
except that it takes an iterable of renderables instead of a single renderable.
Iterate through all the breadcrumb items and separators.
Separators is yielded automatically after each item except the last one. Separator renderables can be overridden with
make_separator_renderable()
.
Bases:
django_cradmin.renderable.AbstractBemRenderable
Wraps a
BreadcrumbItemList
in a box.Get the bem block string.
Get a list of BEM variants.
You do not include the block/element, just the part after
--
.
Bases:
django_cradmin.crbreadcrumb.BreadcrumbItemList
Renders and works just like
BreadcrumbItemList
except that it is wrapped within aBreadcrumbItemListWrapper
.You can subclass
BreadcrumbItemListWrapper
and replace the wrapper class by overridingget_wrapper_renderable_class()
.Parameters: cradmin_instance (django_cradmin.crinstance.BaseCrAdminInstance) – A cradmin instance. Get the renderable for the wrapper element(s).
Returns: BreadcrumbItemListWrapper
or a subclass.Return type: django_cradmin.crbreadcrumb.BreadcrumbItemListWrapper
Get kwargs for the class returned by
get_wrapper_renderable_class()
.Returns: Kwargs. Return type: dict
Render
get_template_names
with the context returned byget_context_data()
.- Paramteters:
- request (HttpRequest): If this is provided, we forward it to
get_context_data()
, and torender_to_string()
(which is used to render the template).
Development¶
Develop django_cradmin, or run the demo¶
To develop django-cradmin, or to run the demo, you will have to do the following.
Clone the git repo¶
You will find the URL on our github project page.
Create a virtualenv¶
$ mkvirtualenv -p /path/to/python3 django_cradmin
Install the development requirements¶
$ workon django_cradmin
$ pip install -r requirements/python3.txt
Create the demo database¶
$ workon django_cradmin
$ inv recreate_devdb
Run the development server¶
$ workon django_cradmin
$ python manage.py runserver
Open http://localhost:8000 and login with:
email: grandma@example.com
password: test
Run the tests¶
$ workon django_cradmin
$ DJANGOENV=test python manage.py test django_cradmin
Using and creating database dumps¶
Importing the test data¶
The easiest method of importing the test database is to use the recreate_devdb
Fabric task:
$ ievv recreate_devdb
Warning
This will destroy your current database.
Users in the test database¶
After importing the test data, you will have some new users. Login to the Django admin UI (http://localhost:8000/admin/) with:
email: grandma@example.com
password: test
and select Users to list all users. The password of all users are test
.
Add new data¶
To add new data, you just need to do add data to the database manually.
Adding data manually (I.E.: Using the Django admin UI)¶
To add data manually, you should first run the recreate_devdb
management
command to make sure you start out with the current up-to-date dataset. Then you
can use the web-UI or the Django shell to add data. Finally, run:
$ ievv dump_db_as_sql
Now you can commit the update django_cradmin/demo/project/demo/dumps/default.sql
to the git
repo if you want to make the changes to the development database available to other developers
of django-cradmin.
Localization/internationalization/translation¶
How we organize the translations¶
All translations are added to django_cradmin/locale/
.
We do not add translation per app for the following reasons:
- There are lots of overlapping translation strings.
- Easier to upload and maintain a single translation catalog on Transifex.
Configure Transifex¶
Before you can start pushing and pulling translation files
to/from Transifex, you will need to create a ~/.transifexrc
. It
should look like this:
[https://www.transifex.com]
hostname = https://www.transifex.com
username = myuser
password = supersecret
token =
More information here: http://docs.transifex.com/developer/client/config.
Translation process¶
We translate using Transifex. This means that the workflow is:
- Mark new translations or change existing translations.
- Build the translation files (
.po
files). - Push translation files (
.po
files) to Transifex. - Wait for translators to translate using Transifex.
- Pull translation files (
.po
files) from Transifex. - Compile translations and commit the
.mo
files.
Below we go in detail for each of these steps. All commands assume the following:
$ cd /path/to/reporoot
$ workon django_cradmin
Mark new translations or change existing translations¶
Read the Django internationalization docs.
Build the translation files¶
First, make sure you have the latest po-files from transifex:
$ tx pull
We have a fabric task for that:
$ inv makemessages
Commit the changes to the .po
-files in django_cradmin/locale/
.
Compile translations and commit the .mo files¶
We have a fabric task for compiling the translations:
$ cd /path/to/reporoot
$ workon django_cradmin
$ inv compilemessages
This should change some .mo
-files in django_cradmin/locale/
.
Commit those files.
How to release a new version of cradmin¶
Note
This assumes you have permission to release cradmin to pypi.
Remove the previous built static files:
$ git rm -r django_cradmin/apps/django_cradmin_js/static/django_cradmin_js/ django_cradmin/apps/django_cradmin_styles/static/django_cradmin_styles/
Update
django_cradmin/version.json
.Run:
$ ievv buildstatic --production
4: Run:
$ git add django_cradmin/apps/django_cradmin_js/static/django_cradmin_js/ django_cradmin/apps/django_cradmin_styles/static/django_cradmin_styles/
- Commit.
- Tag the commit with
<version>
. - Push (
git push && git push --tags
). - Release to pypi (
python setup.py sdist && twine upload dist/django-cradmin-<version>.tar.gz
).
Documentation¶
CRApp¶
-
INDEXVIEW_NAME
= 'INDEX'¶ The name of the app index view (the landing page for the app). We do not enforce this, but we assume that each app has a view with this name.
-
class
Url
(regex, view, kwargs=None, name=None)¶ Bases:
object
Url is mostly the same as func:django.conf.urls.url. You use Url to add urls to an app.
Parameters: - regex – The URL regex.
- view – The view (E.g.:
MyView.as_view()
). - kwargs – Keyword arguments for the view.
- name – The name of the view. This just have to be unique within
the
App
- the actual URL name is generated based on the app name and thedjango_cradmin.crinstance.BaseCrAdminInstance.id
.
-
class
App
(appname, request, active_viewname)¶ Bases:
object
A cradmin App.
Added to a
django_cradmin.crinstance.BaseCrAdminInstance
withdjango_cradmin.crinstance.BaseCrAdminInstance.apps
.-
appname
¶ str – The name of the app.
-
request
¶ django.http.HttpRequest – Django request object for the current request.
-
active_viewname
¶ str – The name of the view we are currently rendering.
-
appurls
= []¶ See
get_appurls()
.
-
active_viewname_is_indexview
()¶ Is the active viewname the app index view?
Returns: True
ifReturn type: bool
-
is_crinstance_rolefrontpage_app
()¶ Is this the
django_cradmin.crinstance.BaseCrAdminInstance.rolefrontpage_appname
Returns: True
if the appname of this app is the same asrequest.cradmin_instance.rolefrontpage_appname
.Return type: bool
-
reverse_appurl
(viewname, args=None, kwargs=None)¶ Works just like
django.core.urlresolvers.reverse()
, except that the name is the name given inappurls
, not the full name of the URL.This means that you should use this to reverse urls within this app.
-
reverse_appindexurl
(args=None, kwargs=None)¶ Shortcut for:
reverse_appurl(crapp.INDEXVIEW_NAME, args=args, kwargs=kwargs)
-
classmethod
build_urls
(cradmin_instance_id, appname)¶ Used internally by
django_cradmin.crinstance.BaseCrAdminInstance.urls()
to build urls for all views in the app.
Add items to the breadcrumb item list.
If you completely override the
get_breadcrumb_item_list_renderable()
method without calling super (or calling this method explicitly), this method will have no effect.Examples:
Simple example:: def add_breadcrumb_list_items(self, breadcrumb_item_list): breadcrumb_item_list.append(url='#', label='Test')
Parameters: breadcrumb_item_list (django_cradmin.crbreadcrumb.BreadcrumbItemList) – The breadcrumb item list to add items to.
Get a breadcrumb item list renderable common for all views within this app.
By default, this just uses
request.cradmin_instance.get_breadcrumb_item_list_renderable()
(seedjango_cradmin.crinstance.BaseCrAdminInstance.get_breadcrumb_item_list_renderable()
).You will normally only want to override this if you want to customize how breadcrumbs are rendered for the views in this app. If you just need to add items to the breadcrumb item list, override
add_breadcrumb_list_items()
.If you override this, remember that the breadcrumb item list from
request.cradmin_instance.get_breadcrumb_item_list_renderable()
can beNone
, so if you use that method you have to remember to handle this.Returns: - A breadcrumb item list renderable object
- or
None
.
Return type: django_cradmin.crbreadcrumb.BreadcrumbItemList
-
CRInstance¶
-
reverse_cradmin_url
(instanceid, appname=None, roleid=None, viewname='INDEX', args=None, kwargs=None)¶ Reverse an URL within a cradmin instance.
Usage is very similar to
django.core.urlresolvers.reverse()
, but you specify the cradmin instance, appname, roleid and viewname instead of the url-nameExamples
Reverse the frontpage on an app:
myapp_index_url = reverse_cradmin_url( instanceid='siteadmin', appname='myapp', roleid=site.id)
Reverse a specific view within an app:
myapp_add_url = reverse_cradmin_url( instanceid='siteadmin', appname='myapp', roleid=site.id, viewname='add')
-
class
FakeRoleFrontpageView
(**kwargs)¶ Bases:
django.views.generic.base.View
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
-
class
BaseCrAdminInstance
(request)¶ Bases:
object
Base class for a django_cradmin instance.
You define a subclass of this to setup a django_cradmin instance.
-
request
¶ HttpRequest – The current HttpRequest.
Parameters: request (HttpRequest) – The current HttpRequest. Stored in request
.-
id
= None¶ The ID of the cradmin instance. Must be unique for the Django instance/site. Must be a string. This is typically a short readable slug that describes what the cradmin instance does. You do not need to specify this except you need to communicate/link between cradmin instances.
-
roleid_regex
= '\\d+'¶ The regex for matching the role id. Defaults to
\d+
.
-
header_renderable_class
¶ alias of
django_cradmin.crheader.DefaultHeaderRenderable
The footer class for this cradmin instance. Must be a subclass of
django_cradmin.crfooter.AbstractFooter
.
-
roleclass
= None¶ The class defining the role for this cradmin instance. If you do not set this, the role system will not be used, which means that you will not get a value in
request.cradmin_role
.
-
rolefrontpage_appname
= None¶ The name of the app that the user should be redirected to after selecting a role. Subclasses MUST eighter specify this or override
rolefrontpage_url()
.
-
flatten_rolefrontpage_url
= False¶ If this is
True
, we do not prefix the urls of therolefrontpage_appname
with the appname. This means that it is hosted on/baseurl/<roleid>/
instead of/baseurl/<roleid>/<appname>/
.If you couple this with setting
roleclass
toNone
, the frontpage will be hosted directly on/baseurl/
.If you set this to
True
, you have to be ensure that the urls of any views within the rolefrontpage app does crash any urls in any of the other apps.
-
apps
= []¶ Apps within the instance. Iterable of
(appname, appclass)
tuples whereappname
is a slug for the app andappclass
is a subclass ofdjango_cradmin.crapp.App
. Can also be specified by overridingget_apps()
.
-
get_cradmin_theme_path
()¶ Return a path to a theme in the same format as
DJANGO_CRADMIN_THEME_PATH
, to use a custom theme for this instance.
-
get_rolequeryset
()¶ Get the roles for the authenticated user.
You get the authenticated user from
self.request.user
.
-
get_titletext_for_role
(role)¶ Get a short title briefly describing the given
role
.
-
get_titletext_for_current_role
()¶ Shortcut for
get_titletext_for_role(role=request.cradmin_role)
.See also
-
get_descriptiontext_for_role
(role)¶ Get a longer description for the given
role
.This is never used directly on its own - it is just the default text used by
get_descriptionhtml_for_role()
. If you want HTML in your description, overrideget_descriptionhtml_for_role()
instead.
-
get_descriptiontext_for_current_role
()¶ Shortcut for
get_descriptiontext_for_role(role=request.cradmin_role)
.See also
-
get_descriptionhtml_for_role
(role)¶ Get a longer description for the given
role
. This is always shown after/belowget_titletext_for_role()
.Defaults to
get_descriptiontext_for_role()
filtered to make it HTML safe and wrapped in a paragraph tag.
-
get_roleid
(role)¶ Get the ID for the given
role
.
-
get_role_from_roleid
(roleid)¶ Get the role for the given
roleid
.Defaults to looking up a
roleclass
object wherepk==roleid
.Returns: A role object or None
.
-
invalid_roleid_response
(roleid)¶ This is called whenever someone requests a role slug that does not exist (if :meth:.`get_role_from_roleid`) returns
None
.Returns: - Defaults to rendering
django_cradmin/invalid_roleid.django.html
.
Return type: django.http.HttpResponse
-
get_role_from_rolequeryset
(role)¶ Returns the given role extracted via the
get_rolequeryset()
queryset.Raises ObjectDoesNotExist if the role is not found in the queryset.
-
missing_role_response
(role)¶ This is called whenever someone requests a role that exists but that they do not have (where meth:.get_role_from_rolequeryset raises
DoesNotExist
).Returns: - Defaults to rendering
django_cradmin/missing_role.django.html
Return type: django.http.HttpResponse
Get the main menu renderable instance.
Defaults to a instance of the class specified in
main_menu_renderable_class
.Returns: An AbstractMenuRenderable object. Return type: django_cradmin.crmenu.AbstractMenuRenderable
Get the expandable menu renderable instance. This is the menu that is expanded by a button which by default is in the main menu.
Defaults to a instance of the class specified in
expandable_menu_renderable_class
.Returns: An AbstractMenuRenderable object. Return type: django_cradmin.crmenu.AbstractMenuRenderable
-
get_header_renderable
(headername='default')¶ Get the header renderable for this cradmin instance.
Defaults to a instance of the class specified in
header_renderable_class
.Returns: An AbstractHeaderRenderable object. Return type: django_cradmin.crheader.AbstractHeaderRenderable See also
django_cradmin.crheader.AbstractHeaderRenderable
.
Get the footer renderable for this cradmin instance.
Defaults to a instance of the class specified in
footer_renderable_class
.Returns: An AbstractHeaderRenderable object. Return type: django_cradmin.crfooter.AbstractHeaderRenderable See also
django_cradmin.crfooter.AbstractHeaderRenderable
.
Add items to the breadcrumb item list.
If you completely override the
get_breadcrumb_item_list_renderable()
method without calling super (or calling this method explicitly), this method will have no effect.Examples:
Simple example:: def add_breadcrumb_list_items(self, breadcrumb_item_list): breadcrumb_item_list.append(url='#', label='Test')
Parameters: breadcrumb_item_list (django_cradmin.crbreadcrumb.BreadcrumbItemList) – The breadcrumb item list to add items to.
Get the breadcrumb item list renderable common for all (or at least most) views within the cradmin instance.
You can override this, or you can set the class in
breadcrumb_item_list_renderable_class
.If you just want to add some items to the breadcrumb, you can override
add_breadcrumb_list_items()
instead.The value returned here is used as the default value for
django_cradmin.crapp.App.get_breadcrumb_item_list_renderable()
(enables apps to add breadcrumb items), which in turn is used bydjango_cradmin.viewhelpers.mixins.CommonCradminViewMixin.get_breadcrumb_item_list_renderable()
(enables views to add breadcrumb items).You can return
None
to not render a breadcrumb at all. Apps and views can still have breadcrumbs, but they will then have to initialize adjango_cradmin.crbreadcrumb.BreadcrumbItemList
in theirget_breadcrumb_item_list_renderable
-methods.Returns: - A breadcrumb item list renderable object
- or
None
.
Return type: django_cradmin.crbreadcrumb.BreadcrumbItemList
-
reverse_url
(appname, viewname, args=None, kwargs=None, roleid=None)¶ Reverse an URL within this cradmin instance.
The advantage over using
django.core.urlresolvers.reverse()
is that you do not need to hardcode the id of the cradmin instance, and that theroleid
is automatically added to args or kwargs (depending on which one you use to pass arguments to the url).Parameters:
-
appindex_url
(appname, args=None, kwargs=None, roleid=None)¶ Reverse the url of the landing page for the given app.
The landing page is the view named
django_cradmin.crapp.INDEXVIEW_NAME
.This would be the same as using
reverse_url()
withviewname=crapp.INDEXVIEW_NAME
.Parameters:
-
rolefrontpage_url
(roleid=None)¶ Returns the URL that the user should be redirected to after selecting a role.
-
get_instance_frontpage_url
()¶ Return the URL of the instance frontpage view.
-
roleselectview_url
()¶ Deprecated, use
get_instance_frontpage_url()
instead.
-
get_common_http_headers
()¶ Override this to set common HTTP headers for all views in the instance.
Returns: A mapping object mapping HTTP header name to value. Returns empty dict by default.
-
has_access
()¶ Check if the given user has access to this cradmin instance.
Defaults to
self.request.user.is_authenticated()
, but you can override this.
-
get_two_factor_auth_viewname
()¶ Get the two-factor authentication view specified in settings with
DJANGO_CRADMIN_TWO_FACTOR_AUTH_VIEWNAME
Returns: The viewname if specified in settings, else it returns None
.
-
get_foreignkeyselectview_url
(model_class)¶ Get foreign key select view URL for the given model class.
This can be used by foreign key select widgets to lookup a view for this model within the current instance.
By default this returns
None
, so you have to override this if you want to use it.Parameters: model_class – A django.db.models.Model
subclass.
-
get_manytomanyselectview_url
(model_class)¶ Get many-to-many select view URL for the given model class.
This can be used by many-to-many widgets, like
django_cradmin.widgets.modelmultichoice.ModelMultiChoiceWidget
, to lookup a view for this model within the current instance.By default this returns
None
, so you have to override this if you want to use it.Parameters: model_class – A django.db.models.Model
subclass.
-
classmethod
get_roleselect_viewclass
()¶ Get the viewclass for the roleselect view.
Returns: - Defaults to
django_cradmin.views.roleselect.RoleSelectView
, - but any subclass of
django.views.View
can be used.
Return type: django.views.View - Defaults to
-
classmethod
get_roleselect_view
()¶ Get the view for selecting role.
Instanciates the view class returned by
get_roleselect_viewclass()
, and decorates the view withdjango_cradmin.decorators.has_access_to_cradmin_instance()
.You should not need to override this, override
get_roleselect_viewclass()
instead.Note
The name of the URL for this view is
<cradmin instance id>-roleselect
, where<cradmin instance id>
isid
. You can reverse the URL of this view withget_instance_frontpage_url()
.
-
classmethod
urls
()¶ Get the url patterns for the cradmin instance.
-
add_extra_instance_variables_to_request
(request)¶ Override this method to add extra attributes to the request object for all views in this cradmin instance.
This is called by the decorator that wraps all views within the instance.
-
get_body_css_classes_list
()¶ Get the css classes for the
<body>
element that this cradmin instance should add for all views as a list.Returns an empty list by default, but you should override this to add css classes to the
<body>
element for all views within this instance.
-
get_body_css_classes_string
()¶ Get the css classes for the
<body>
element that this cradmin instance should add for all views as a string.You should not override this - override
get_body_css_classes_list()
. This method only joins the list returned by that method to make it easier to use in Django templates.
-
page_cover_bem_block
¶ Get the name of the BEM block for the page cover.
Should be overridden if you do not want the default of
adminui-page-cover
.If you need more complex behavior, you should consider:
- Making your own templates that extend:
django_cradmin/standalone-base.django.html
- for all the views outside the role.django_cradmin/base.django.html
- for all the views within the role
- OR override (this affects all cradmin instances):
django_cradmin/standalone-base.django.html
django_cradmin/base.django.html
-
get_default_javascriptregistry_component_ids
()¶ Get default component IDs for all views within this cradmin instance.
Defaults to
['django_cradmin_javascript']
.
-
get_default_within_role_javascriptregistry_component_ids
()¶ Get default component IDs for all views within a cradmin_role within this cradmin instance.
Defaults to
get_default_javascriptregistry_component_ids()
.
-
-
class
NoRoleMixin
¶ Bases:
object
Mixin to make a
BaseCrAdminInstance
not require a role.Must be mixed in before
BaseCrAdminInstance
.
-
class
NoLoginMixin
¶ Bases:
object
Mixin to make a
BaseCrAdminInstance
not require login.Must be mixed in before
BaseCrAdminInstance
.-
has_access
()¶ We give any user access to this instance, including unauthenticated users.
-
-
class
NoRoleNoLoginCrAdminInstance
(request)¶ Bases:
django_cradmin.crinstance.NoRoleMixin
,django_cradmin.crinstance.NoLoginMixin
,django_cradmin.crinstance.BaseCrAdminInstance
- Shortcut for creating a
BaseCrAdminInstance
with the NoRoleMixin
andNoLoginMixin
.
Parameters: request (HttpRequest) – The current HttpRequest. Stored in request
.- Shortcut for creating a
Settings¶
DJANGO_CRADMIN_THEME_PATH¶
The staticfiles path to the theme CSS. If this is not
set, we use django_cradmin/dist/css/cradmin_theme_default/theme.css
.
DJANGO_CRADMIN_CSS_ICON_MAP¶
A dictionary mapping generalized icon names to css classes.
It is used by the cradmin_icon
template tag. If you do
not set this, you will get font-awesome icons as defined
in django_cradmin.css_icon_map.FONT_AWESOME
.
See also
DJANGO_CRADMIN_CSS_ICON_LIBRARY_PATH¶
The staticfiles path to the css icon library.
Defaults to "django_cradmin/dist/vendor/fonts/fontawesome/css/font-awesome.min.css"
.
DJANGO_CRADMIN_HIDE_PAGE_HEADER¶
If this is True
, we do not render the page header. This only affects views
that use templates inheriting from the django_cradmin/standalone-base.django.html
template. This means all the views in django_cradmin.viewhelpers
, but not the login
views, or other standalone (non-crapp.App views).
DJANGO_CRADMIN_INCLUDE_TEST_CSS_CLASSES¶
Enable CSS classes for unit tests? The CSS classes added
using the django_cradmin.templatetags.cradmin_tags.cradmin_test_css_class()
template
tag is not included unless this is True
. Should only be True
when running
automatic tests. Defaults to False
.
DJANGO_CRADMIN_DEFAULT_HEADER_CLASS¶
The default header class rendered in the header
block of
the base template for all cradmin view templates (standalone-base-internal.django.html).
If this is None
, or not specified (the default), we do not render
a header.
Must be the string path to a subclass of
django_cradmin.crheader.AbstractHeaderRenderable
or a callable with the same
signature as the AbstractHeaderRenderable constructor.
A callable is typically used to dynamically determine the header
based on the provided kwargs. The callable must return an object
of django_cradmin.crheader.AbstractHeaderRenderable
or a subclass.
DJANGO_CRADMIN_DEFAULT_STATIC_COMPONENT_IDS¶
List of static components registered with the
django_cradmin.javascriptregistry.registry.Registry
singleton
that should be available by default in all templates extending
the standalone-base-internal.django.html
template unless
something else is specified by the view or cradmin instance.
uicontainer¶
DJANGO_CRADMIN_UICONTAINER_VALIDATE_BEM¶
Set this to False
in production to disable validation of
BEM blocks and elements. See
django_cradmin.uicontainer.container.AbstractContainerRenderable.should_validate_bem()
.
for more details.
DJANGO_CRADMIN_UICONTAINER_VALIDATE_DOM_ID¶
Set this to False
in production to disable validation of
DOM ids. See
django_cradmin.uicontainer.container.AbstractContainerRenderable.should_validate_dom_id()
.
for more details.
Template tags¶
cradmin_tags¶
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.get_titletext_for_role()
.
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.get_titletext_for_role()
.
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.rolefrontpage_url()
.
Template tag implementation of
django_cradmin.crapp.App.reverse_appurl()
.Examples
Reverse the view named
"edit"
within the current app:{% load cradmin_tags %} <a href='{% cradmin_appurl "edit" %}'>Edit</a>
Reverse a view with keyword arguments:
{% load cradmin_tags %} <a href='{% cradmin_appurl "list" mode="advanced" orderby="name" %}'> Show advanced listing ordered by name </a>
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.appindex_url()
.Examples
Reverse index (frontpage) of current app:
{% load cradmin_tags %} <a href='{% cradmin_appindex_url %}'> Go to pages-app </a>
Reverse a view with keyword arguments:
{% load cradmin_tags %} <a href='{% cradmin_appindex_url mode="advanced" orderby="name" %}'> Show advanced listing ordered by name </a>
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.appindex_url()
.Examples
Reverse index (frontpage) of the
"pages"
app:{% load cradmin_tags %} <a href='{% cradmin_instance_appindex_url appname="pages" %}'> Go to pages-app </a>
Reverse a view with keyword arguments:
{% load cradmin_tags %} <a href='{% cradmin_instance_appindex_url appname="pages" mode="advanced" orderby="name" %}'> Show advanced listing ordered by name </a>
Template tag implementation of
django_cradmin.crinstance.BaseCrAdminInstance.reverse_url()
.Examples
Reverse the view named
"edit"
within the app named"pages"
:{% load cradmin_tags %} <a href='{% cradmin_instance_url appname="pages" viewname="edit" %}'> Edit </a>
Reverse a view with keyword arguments:
{% load cradmin_tags %} <a href='{% cradmin_instance_url appname="pages" viewname="list" mode="advanced" orderby="name" %}'> Show advanced pages listing ordered by name </a>
Get the URL of the cradmin instance with the provided
instanceid
.Parameters: instanceid – The id
if adjango_cradmin.crinstance.BaseCrAdminInstance
.
Template tag implementation of
django_cradmin.crinstance.reverse_cradmin_url()
.Examples
Reverse the view named
"edit"
within the app named"pages"
in the cradmin-instance with id"my_cradmin_instance"
using roleid10
:{% load cradmin_tags %} <a href='{% cradmin_url instanceid="my_cradmin_instance" appname="pages" roleid=10 viewname="edit" %}'> Edit </a>
Reverse a view with keyword arguments:
{% load cradmin_tags %} <a href='{% cradmin_url instanceid="my_cradmin_instance" appname="pages" roleid=10 viewname="list" mode="advanced" orderby="name" %}'> Show advanced pages listing ordered by name </a>
Template filter that converts a json serializable object to a json encoded string.
Template tag that converts a json serializable object to a json encoded string and quotes it for use as an attribute value.
Examples
Typical usage:
{% load cradmin_tags %} <div data-something={% cradmin_jsonencode_html_attribute_value serializableobject %}></div>
Notice that we do not add
"
around the value - that is done automatically.
Render a
django_cradmin.renderable.AbstractRenderable
.Unlike just using
{{ renderable.render }}
, this sends therequest
into render (so this is the same as callingrenderable.render(request=context['request'])
.Examples
Render a renderable named
renderable
in the current template context:{% load cradmin_tags %} {% cradmin_render_renderable renderable %}
Adds a CSS class for automatic tests. CSS classes added using this template tag is only included when the the
DJANGO_CRADMIN_INCLUDE_TEST_CSS_CLASSES
setting is set toTrue
.To use this template tag, you provide a
suffix
as input, and the output will be `` test-<suffix> ``. Notice that we include space before and after the css class - this means that you do not need to add any extra spaces within your class-attribute to make room for the automatic test only css class.Examples
Use the template tag to add test only css classes:
{% load cradmin_tags %} <p class="paragraph paragraph--large{% cradmin_test_css_class 'introduction' %}"> The introduction </p>
Ensure that your test settings have
DJANGO_CRADMIN_INCLUDE_TEST_CSS_CLASSES = True
.Write tests based on the test css class:
from django import test import htmls class TestCase(test.TestCase): def test_introduction(self): response = some_code_to_get_response() selector = htmls.S(response.content) with self.assertEqual( 'The introduction', selector.one('test-introduction')
Parameters: suffix – The suffix for your css class. The actual css class will be `` test-<suffix> ``.
Joins a list of css classes.
Parameters: css_classes_list (list) – List or other iterable of css class strings. Examples
Simple example:
{% cradmin_join_css_classes_list my_list_of_css_classes %}
Render a header.
Parameters: - context – template context.
- headername (list) – List or other iterable of css class strings.
Sent to
django_cradmin.crinstance.BaseCrAdminInstance.get_header_renderable()
to get the header. - include_context – Forwarded to
cradmin_render_renderable()
. - **kwargs – Forwarded to
cradmin_render_renderable()
.
Examples
Render the default header:
{% cradmin_render_header %} ... or ... {% cradmin_render_header headername='default' %}
Render a custom header:
{% cradmin_render_header headername='myheader' %}
The last example assumes that you have overridden
django_cradmin.crinstance.BaseCrAdminInstance.get_header_renderable()
to handle this headername as an argument.
Render breadcrumbs from the
cradmin_breadcrumb_item_list
template context variable.If
cradmin_breadcrumb_item_list
is not in the template context, or if the cradmin_breadcrumb_item_list is empty (has no breadcrumb items), nothing is rendered.Parameters: - context – template context.
- include_context – Forwarded to
cradmin_render_renderable()
. - location (str) – The location to render the breadcrumb item list.
If this is
None
(the default), we render the list no matter what. If it has a value, the value must match theget_location()
of thecradmin_breadcrumb_item_list
item list. - **kwargs – Forwarded to
cradmin_render_renderable()
.
Examples
Render the breadcrumbs:
{% cradmin_render_breadcrumb_item_list %}
Render the default header specified via the :setting:DJANGO_CRADMIN_DEFAULT_HEADER_CLASS` setting.
Uses
django_cradmin.crheader.get_default_header_renderable()
to get the header renderable.
Render the default footer specified via the :setting:DJANGO_CRADMIN_DEFAULT_FOOTER_CLASS` setting.
Uses
django_cradmin.crfooter.get_default_footer_renderable()
to get the footer renderable.
Render the default header specified via the :setting:DJANGO_CRADMIN_DEFAULT_EXPANDABLE_MENU_CLASS` setting.
Uses
django_cradmin.crmenu.get_default_expandable_menu_renderable()
to get the expandable menu renderable.
cradmin_icon_tags¶
The cradmin_icon_tags
Django template tag library defines tags that makes
it easy to swap out the icons used by the provided Django cradmin components.
It is used like this:
{% load cradmin_icon_tags %}
<span class="{% cradmin_icon 'search' %}"></span>
where {% cradmin_icon 'search' %}
will look up css classes for the
icon in the DJANGO_CRADMIN_CSS_ICON_MAP
Django setting.
If DJANGO_CRADMIN_CSS_ICON_MAP
is not set, we default to
django_cradmin.css_icon_map.FONT_AWESOME
, but you can
easily provide your own with something like this in your settings.py:
from django_cradmin import css_icon_map
DJANGO_CRADMIN_CSS_ICON_MAP = css_icon_map.FONT_AWESOME.copy()
DJANGO_CRADMIN_CSS_ICON_MAP.update({
'search': 'my my-search-icon'
})
You can even add your own icons and use cradmin_icon
for your own
views/components.
-
FONT_AWESOME
= {'bold': 'fa fa-bold', 'caret-down': 'fa fa-caret-down', 'caret-up': 'fa fa-caret-up', 'close-overlay-right-to-left': 'fa fa-chevron-left', 'codeblock': 'fa fa-code', 'h1': 'fa fa-header django-cradmin-icon-font-lg', 'h2': 'fa fa-header', 'h3': 'fa fa-header django-cradmin-icon-font-sm', 'italic': 'fa fa-italic', 'link': 'fa fa-link', 'list-ol': 'fa fa-list-ol', 'list-ul': 'fa fa-list-ul', 'loadspinner': 'fa fa-spin fa-spinner', 'pager-next-page': 'fa fa-chevron-right', 'pager-previus-page': 'fa fa-chevron-left', 'search': 'fa fa-search', 'select-left': 'fa fa-angle-right', 'select-right': 'fa fa-angle-left', 'x': 'fa fa-times'}¶ Font-awesome icon map for the
cradmin_icon
template tag.
cradmin_email_tags¶
Creating a custom theme¶
Getting started¶
TODO
SASS style guide¶
- Indent with 4 spaces.
- Use BEM naming syntax. See http://getbem.com/ and http://cssguidelin.es/#bem-like-naming.
- Document everything with PythonKSS.
- Use
md
,lg
,xl
, … (as part of modifier name) for breakpoints. - Use
small
,large
,xlarge
, … (as part of modifier name) for sizes. - Never use
@extend
to extend a component. Components should be as isolated as possible. They may require another component to be useful, but they should not extend another component.
PythonKSS documentation style guide¶
Never use numbers for section references¶
Use <setting|generic|base|comonent>.<BEM-block>
.
E.g: component.modal
.
Define dependencies last in the description¶
Define dependencies last in the description as follows:
/* Something
Some description.
# Depends on
- component.modal
- component.backdrop
Styleguide something.something
*/
HTML style guide¶
- Never use ID for styling.
- Prefix IDs with
id_
, and write ids in all lowercase with words separated by a single_
. Example:id_my_cool_element
. - Separate css classes with two spaces. Example:
<div class="class1 class2">
Icons¶
How it works¶
Icon names are virtual (icon package agnostic). The default icon names are defined in:
django_cradmin/apps/django_cradmin_styles/staticsources/django_cradmin_styles/styles/basetheme/1__settings/_cricon.scss
When adding support for an icon package (font-awesome, ionicons, …), we need
to implement a set of mixins, and import those mixins before we import basetheme/3__base/all
.
We supply an implementation for font-awesome by default. If you just want to use fon
Extending the default font-awesome icon set¶
This is fairly easy. You just need to add mapping from a virtual name
to a font-awesome variable for the icon in $cricon-font-awesome-free-icon-map
,
and add the virtual names to $cricon-extra-icon-names
.
Example - adding the align right and align center icons from font-awesome:
@import 'basetheme/3__base/cricon/cricon-font-awesome';
$cricon-font-awesome-free-icon-map: map-merge($cricon-font-awesome-free-icon-map, (
align-center: $fa-var-align-center,
align-right: $fa-var-align-right
));
$cricon-extra-icon-names: align-center, align-right;
@import 'basetheme/3__base/all';
With this, cricon--align-center
and cricon--align-right
css classes will be available.
Adding support for another icon set¶
Take a look at the _cricon-font-awesome.scss
file - you need to implement all of the
mixins from that, and import your custom icon mixins instead of _cricon-font-awesome.scss
.
Releasenotes¶
Django cradmin 1.1.0 releasenotes¶
What is new?¶
- Django 1.9 support.
- Also tested with Django 1.10b1, and all tests pass, and everything seems to work as it should.
- Minumum Django version is still 1.8.
- Minimum version of Django-crispy-forms updated to 1.6.0.
Breaking changes¶
The cradmin_texformatting_tags
template library was removed¶
It used deprecated and non-public APIs. It is not in use anywhere in the library, and was undocumented, so this should hopefully not cause any major issues for any users.
Custom themes must update their cradmin_base-folder¶
Because of some minor changes in form rendering in django-crispy-forms
1.6.0, all custom
themes must update their cradmin_base
folder to the base folder. The changes was made
in commit d7b0c061e805431d01d4a48dd7def8c6ad2414ba.
Can no longer set button classes using field_classes¶
This is due to changes in django-crispy-forms
1.6.0. Buttons inheriting from
django_cradmin.crispylayouts.CradminSubmitButton
(or PrimarySubmit, DangerSubmit, DefaultSubmit, …),
must set their CSS classes using the button_css_classes
attribute instead.
Django cradmin 1.1.1 releasenotes¶
What is new?¶
django_cradmin.viewhelpers
can now be imported with from django_cradmin import viewhelpers
.
Example:
from django_cradmin import viewhelpers
class MyCreateView(viewhelpers.create.CreateView):
pass # more code here ...
The imported viewhelpers
object does not include listbuilder
, listfilter
or multiselect2
,
they should still be imported using from django_cradmin.viewhelpers import <module>
.
Breaking changes¶
There are no breaking changes between 1.1.0 and 1.1.1.
Django cradmin 2.0.0 releasenotes¶
What is new?¶
- New theme. No longer based on bootstrap, and using SASS instead of LESS.
django_cradmin.viewhelpers
can be imported asfrom django_cradmin import viewhelpers
. After this import, you can useviewhelpers.create.CreateView
,viewhelpers.update.UpdateView
, …- Views using any of the base templates for cradmin must inherit from one of the views in
django_cradmin.viewhelpers
or mix in one of the subclasses ofdjango_cradmin.javascriptregistry.viewmixin.MinimalViewMixin
. - New menu system. Much more flexible, with a much simpler core based on
django_cradmin.viewhelpers.listbuilder
.
Django base template changes¶
standalone-base-internal.django.html¶
- The div with id
django_cradmin_bodycontentwrapper
no longer exists, and this also means that theoutside-bodycontentwrapper
template block no longer exists. - We no longer load any javascript by default.
django_cradmin/base-internal.django.html¶
- We use the new template blocks from
standalone-base-internal.django.html
(see the section above). - The
pageheader
andpageheader-inner
blocks no longer exist. Use: page-cover
instead ofpageheader
.page-cover-content
instead ofpageheader-inner
, or usepage-cover-title
to just set the content of the H1 tag.
- The
You can use the following regex in PyCharm to searh and all replace page-header-content
blocks that only contain a H1 tag with the new page-cover-title
block:
Text to find:
\{\% block pageheader\-inner \%\}\s*\<h1\>\s*([^>]+?)\s*\<\/h1\>\s*\{\% endblock pageheader\-inner \%\}Replace with:
\{\% block page-cover-title \%\}\n $1\n\{\% endblock page-cover-title \%\}
The regex in Text to find is both Python an java compatible, so you should be able to create a python script to handle this if needed.
Recommended migration route:
Replace pageheader-inner with the regex above.
Search for pageheader-inner, and update the more complex cases manually to use something like this:
{% block page-cover-title %} My title {% endblock page-cover-title %} {% block page-cover-content %} {{ block.super }} Stuff below the title in the old pageheader-inner block. {% endblock page-cover-content %}
layouts/standalone/focused.django.html¶
The innerwrapper_pre
and innerwrapper_post
blocks no longer exists. You
will typically want to update templates using these blocks with:
{% extends "django_cradmin/focused.django.html" %}
{% block body %}
{# The content you had in innerwrapper_pre here #}
{{ block.super }}
{# The content you had in innerwrapper_pre here #}
{% endblock body %}
If you want the pre and post content to line up with the focused content,
wrap them in section tags with the page-section page-section--tight
css classes:
{% extends "django_cradmin/focused.django.html" %}
{% block body %}
<section class="page-section page-section--tight">
{# The content you had in innerwrapper_pre here #}
</section>
{{ block.super }}
<section class="page-section page-section--tight">
{# The content you had in innerwrapper_post here #}
</section>
{% endblock body %}
CSS class changes¶
The css framework is completely new, so all CSS classes have new names and they are structured differently. This section has a
Removed css classes¶
django-cradmin-listbuilder-floatgridlist
: This was never ready to use out of the box, and it is better to create this per app to make it work perfectly with whatever javascript library required to handle the layout.
Listbuilder lists¶
Listbuilder lists use the new list
css class. Unlike the old django-cradmin-listbuilder-list
css
class, this does not override typography styles. Instead it only focus on layout-specific styles.
This means that you need to use css classes to style heading elements unless you want them to have their original sizes.
Deprecated in the python library¶
- django_cradmin.crmenu.MenuItem.get_active_item_wrapper_tag is deprecated. Use
django_cradmin.crmenu.MenuItem.get_menu_item_active_htmltag()
.
Removed from the python library¶
django_cradmin.viewhelpers.listbuilder.lists.FloatGridList
is removed for the reason explained for thedjango-cradmin-listbuilder-floatgridlist
css class above.
Changes in the template tags¶
- The
django_cradmin.templatetags.cradmin_tags.cradmin_theme_staticpath
template tag raises an exception ifrequest
is not in the template context.
Django cradmin 3.0.0 releasenotes¶
Note
This release marks the end of long lasting major releases. From now on we will release a major release when adding anything major, or when introducing any changes that require some kind of manual or dangerous update of existing projects.
Whats new¶
- Add a new set of icon mixins and standard icons. Font-awesome is implemented as the default, but adding other icon sets is fairly easy. This is documented in Creating a custom theme.
- Update the javascript with the new icon css classes.
Note
There is no changes to the Python code, only so the theme .scss and javascript files.
Migrate from 2.x to 3.x¶
Update .scss files¶
After updating to 3.x, your custom themes will break. You will need to update
so that you include @import 'basetheme/3__base/cricon/cricon-font-awesome';
before
@import 'basetheme/3__base/all';
.
So if you had the following with cradmin 2.x:
@import 'basetheme/1__settings/all';
@import '1__settings/all';
@import 'basetheme/2__generic/all';
@import '2__generic/all';
@import 'basetheme/3__base/all';
@import '3__base/all';
@import 'basetheme/4__components/all';
@import '4__components/all';
You will have the following with 3.x+:
@import 'autogenerated_partials/variables';
@import 'basetheme/1__settings/all';
@import '1__settings/all';
@import 'basetheme/2__generic/all';
@import '2__generic/all';
@import 'basetheme/3__base/cricon/cricon-font-awesome'; // This line is new!
@import 'basetheme/3__base/all';
@import '3__base/all';
@import 'basetheme/4__components/all';
@import '4__components/all';
Furthermore, if you have a custom $font-size
variable you need to ensure you have
all the required sizes in the map:
- xxsmall
- xsmall
- small
- medium
- large
- xlarge
- xxlarge
- xxxlarge
- smallcaps
Update javascript¶
You need to update your package.json to have “django_cradmin_js”: “^3.0.0” as a requirement.
Django cradmin 4.0.0 releasenotes¶
What is new?¶
- Cleanup of most of the leftovers from cradmin 1.x
cradmin_legacy
compatibility. The cradmin_legacy library is a fork of the django_cradmin 1.x branch, which can be used alongside django_cradmin >= 4.0.0.
Migrate from 3.x to 4.x¶
Update for ievv buildstatic builds¶
If you use ievv buildstatic
to build your sass, you need to update to ievv-opensource >= 4.5.0
.
You can, and should:
update your
ievvbuildstatic.sassbuild.Plugin
in theIEVVTASKS_BUILDSTATIC_APPS
setting tocradmin_ievvbuildstatic.SassBuild
. You will also need to addfrom django_cradmin.utils import cradmin_ievvbuildstatic
.Remove:
ievvbuildstatic.mediacopy.Plugin( sourcefolder=ievvbuildstatic.filepath.SourcePath('django_cradmin_styles', 'media'), destinationfolder='media' ),
Add
@import 'autogenerated_partials/variables';
to the top of yourmain.scss
, or whatever your main scss source file is.
The reason why you do not need to mediacopy the “media” directory is that cradmin_ievvbuildstatic.SassBuild
sets the sass variable $media-path
to point to the files from django_cradmin_styles. The $media-path
variable is imported from autogenerated_partials/variables
.
Deprecated apps, and how to handle the deprecation¶
Deprecated: cradmin_imagearchive¶
The django_cradmin.apps.cradmin_imagearchive
app has been deprecated
and moved to django_cradmin.deprecated_apps.cradmin_imagearchive`,
and the database model has been renamed from ``ArchiveImage
to
ArchiveImageDeprecated
.
The cradmin_imagearchive
module should have been removed in django_cradmin 2.0.0,
but it was forgotten. If you need to use this app, you should consider:
- Installing
cradmin_legacy
. Make sure you follow the guides in thecradmin_legacy
for adding it to an existingdjango_cradmin > 2.0
install. - Change from
django_cradmin.apps.cradmin_imagearchive
->cradmin_legacy.apps.cradmin_imagearchive
inINSTALLED_APPS
.
This should just work, since the appnames are the same, and their migrations match.
If you do not want to depend on cradmin_legacy
, you can update your INSTALLED_APPS
with django_cradmin.apps.cradmin_imagearchive
-> django_cradmin.deprecated_apps.cradmin_imagearchive
.
When you migrate this change, the database table for ArchiveImage will be renamed so
that it ends with deprecated
. This avoids problems if you add cradmin_legacy
in the future.
Deprecated: cradmin_temporaryfileuploadstore¶
The django_cradmin.apps.cradmin_temporaryfileuploadstore
app has been deprecated
and moved to django_cradmin.deprecated_apps.cradmin_temporaryfileuploadstore`,
and the database models has been renamed from ``TemporaryFileCollection
->
TemporaryFileCollectionDeprecated
and TemporaryFile
-> TemporaryFileDeprecated
.
The cradmin_temporaryfileuploadstore
module should have been removed in django_cradmin 2.0.0,
but it was forgotten. If you need to use this app, you should consider:
- Installing
cradmin_legacy
. Make sure you follow the guides in thecradmin_legacy
for adding it to an existingdjango_cradmin > 2.0
install. - Change from
django_cradmin.apps.cradmin_temporaryfileuploadstore
->cradmin_legacy.apps.cradmin_temporaryfileuploadstore
inINSTALLED_APPS
.
This should just work, since the appnames are the same, and their migrations match.
If you do not want to depend on cradmin_legacy
, you can update your INSTALLED_APPS
with django_cradmin.apps.cradmin_temporaryfileuploadstore
-> django_cradmin.deprecated_apps.cradmin_temporaryfileuploadstore
.
When you migrate this change, the database tables will be renamed so that they end with deprecated
.
This avoids problems if you add cradmin_legacy
in the future.
Django cradmin 5.0.0 releasenotes¶
What is new?¶
- Generalized breadcrumbs support in the new
crbreadcrumb
module. See crbreadcrumb — Generalized breadcrumb rendering. - Docs for all the non-deprecated classes in
django_cradmin.viewhelpers
. - Renamed
adminui-expandable-menu
BEM block toexpandable-menu
. - New neutral variable in the theme colormap -
verylight
.
Migrate from 4.x to 5.0.0¶
Handle adminui-breadcrumbs BEM block removal¶
The adminui-breadcrumbs
css BEM block (set of css classes) has been removed.
You should be able to find occurrences of the adminui-breadcrumbs
css classes fairly easily with
something like:
$ git grep adminui-breadcrumbs -- '*.html' '*.py' '*.js' '*.jsx' '*.scss'
$ git grep breadcrumbs-mixin -- '*.html' '*.py' '*.js' '*.jsx' '*.scss'
If your project is affected by the removal of adminui-breadcrumbs
, you have 3 options:
- Rewrite your code to using the new
crbreadcrumb
module - see crbreadcrumb — Generalized breadcrumb rendering. This is the recommended solution. - Rewrite your code to use the
breadcrumb-item-list
instead ofadminui-breadcrumbs
. This may not work in all cases since they are not 100% compatible. - Copy the
.adminui-breadcrumbs
css class from _adminui-breadcrumbs.scss, and thebreadcrumbs-mixin
mixin class from _mixins.scss in the 4.x branch from the django cradmin repo. This is not recommended, but it could be a usable intermediate solution if you have a lot of refactoring to do.
Handle the new verylight
neutral color¶
Add the verylight
color to the neutral key in the $colors
map in your
SASS sources. Example:
$colors: (
// ...
neutral: (
// ...
verylight: tint($__color-neutral-base, 75%),
// ...
),
// ...
);
Django cradmin 5.1.0 releasenotes¶
What is new?¶
- Various bug fixes in django_cradmin_js.
crmenu
module: Use cricon for the menu toggle.delay_middleware
module: Use the “new” django middleware format.- loading-indicator (both css and javascript): Support visible message, not just screenreader message.
- button styles: Make it easier to create custom button styles with some new mixins.
page-cover-mixin
SASS mixin: Add __button BEM element.- Add new
block
BEM block. viewhelpers.listbuilder
: Fix refactoring bug that basically made this module unusable.crbreadcrumb
: Hide in print by default.
Migrate from 5.0.0 to 5.1.0¶
The only thing that may cause problems would be the cricon in the crmenu menu toggle if you have
customized that a lot. In that case, you will probably have to create your own
subclass of django_cradmin.crmenu.MenuToggleItemItemRenderable
.
Django cradmin 5.2.0 releasenotes¶
What is new?¶
- Rename the
DJANGO_CRADMIN_DEFAULT_EXPANDABLE_CLASS
setting toDJANGO_CRADMIN_DEFAULT_EXPANDABLE_MENU_CLASS
. - Improved support for expandable menu outside of a cradmin instance.
- Add support for a default footer rendererable. See
DJANGO_CRADMIN_DEFAULT_FOOTER_CLASS
. django_cradmin_styles
:- Remove the need for the $footer-height SASS variable. We have wrapped the body content
with a new
fill-viewport-layout
BEM block, which handles footer alignment without the need for knowing the height of the footer. - Handle SVG image sizing correctly in IE 11 in page-header-mixin
__brandimage
. - Add two new cricon icons: comment and comments.
- Remove the need for the $footer-height SASS variable. We have wrapped the body content
with a new
- Supports adding extra user attributes before cleaning in
AbstractCreateAccountForm
.
Migrate from 5.1.0 to 5.2.0¶
If you set or use the DJANGO_CRADMIN_DEFAULT_EXPANDABLE_CLASS
setting, refactor to
to DJANGO_CRADMIN_DEFAULT_EXPANDABLE_MENU_CLASS
. Something like this should help you
find ocurrences:
$ git grep DJANGO_CRADMIN_DEFAULT_EXPANDABLE_CLASS -- '*.html' '*.py'