In my Django app I have a series of generic views (create, update, delete, detail, list) that most of my actual views inherit from. All of these views add a number of pieces of useful information to the context (the singular and plural names of the model, the urls for creating, listing, etc). But the views more-or-less all duplicate the same code. For that reason I'd like to move these things to a context processor and remove the code duplication.
My issue is, I can't seem to determine the things I would need from the request that's passed to the context processor (ie: if I could access the model instance, model class, form class, etc), then Id be fine. The code below shows what the get_context_data looks like within the views - how would I replicate this in a context processor?
Thanks.
def get_context_data(self, **kwargs):
"""Passes context variables to the HTML templates."""
context = super(CodexAnonDetailView, self).get_context_data(**kwargs)
context['model_name'] = model_ngettext(self.model, 1)
context['model_name_plural'] = model_ngettext(self.model, 2)
context['object_create_url'] = reverse('%s:%s_create' % (resolve(self.request.path).app_name, self.model.__name__))
context['model_list_url'] = reverse('%s:%s_list' % (resolve(self.request.path).app_name, self.model.__name__))
The beauty of Django's class based views is that you can take advantage of class inheritance to implement shared functionality
Define a class that implements the behavior you require and then inherit from both the generic view class and your mixin
class MyMixin(object):
def get_context_data(self, **kwargs):
context = super(MyMixin, self).get_context_data(**kwargs)
context['model_name'] = model_ngettext(self.model, 1)
context['model_name_plural'] = model_ngettext(self.model, 2)
context['object_create_url'] = reverse('%s:%s_create' % (resolve(self.request.path).app_name, self.model.__name__))
context['model_list_url'] = reverse('%s:%s_list' % (resolve(self.request.path).app_name, self.model.__name__))
return context
class MyCreateView(MyMixin, CreateView):
model = MyModel
Related
Is there a way to access the model instance that is going to be presented in a generic.DetailView in views.py before the template gets rendered? Something like the hypothetical function here:
class MyModelDetailView(LoginRequiredMixin, generic.DetailView):
model = MyModel
template_name_suffix = '_details'
def do_some_initial_stuff(model_instance):
# do stuff to model_instace,
# like assigning fields, creating context variables based on existing fields, etc.
Ultimately, I would like have a user click a button for a specific model instance in one template, then get directed to this generic.DetailView template where the model is presented with some of its field values changed and some other stuff (eg. the model may have a field that acknowledges that the this user clicked the button in the previous template). Suggestions on the most efficient way to do this would be appreciated. Thanks :)
If you want to make certain changes only if a button is clicked in the previous view then you can make use of the Django sessions. Add the needed value to the sessions dict.
request.session['button_clicked'] = True
For making changes in the current context of view it can be done by overriding get_context_data().
Like:
class MyModelDetailView(LoginRequiredMixin, generic.DetailView):
model = MyModel
template_name_suffix = '_details'
def get_context_data(self):
context = super(MyModelDetailView, self).get_context_data()
# Object is accessible through self.object or self.get_object()
if request.session.pop('button_clicked', False):
myobject = self.object
# The alteration you make on myobject wont be saved until you save it using myobject.save()
# Alter the object using myobject and alter the context varible like this:
context['object'] = myobject
context['myvar'] = "My Variable"
# Then return
return context
More about Django Sessions: https://docs.djangoproject.com/en/1.11/topics/http/sessions/
I have just started messing with class based views and I would like to be able to access variables from the URL inside my class. But I am having difficulties getting this to work. I saw some answers but they were all so short I found them to be of no help.
Basically I have a url
url(r'^(?P<journal_id>[0-9]+)/$',
views.Journal_Article_List.as_view(),
name='Journal_Page'),
Then I would like to use ListView to display all articles in the particular journal. My article table however is linked to the journal table via a journal_id. So I end up doing the following
class Journal_Article_List(ListView):
template_name = "journal_article_list.html"
model = Articles
queryset = Articles.objects.filter(JOURNAL_ID = journal_id)
paginate_by = 12
def get_context_data(self, **kwargs):
context = super(Journal_Article_List, self).get_context_data(**kwargs)
context['range'] = range(context["paginator"].num_pages)
return context
The journal_id however is not passed on like it is in functional views. From what I could find on the topic I read I can access the variable using
self.kwargs['journal_id']
But I’m kind of lost on how I am supposed to do that. I have tried it directly within the class which lets me know that self does not exist or by overwriting get_queryset, in which case it tells me as_view() only accepts arguments that are already attributes of the class.
If you override get_queryset, you can access journal_id from the URL in self.kwargs:
def get_queryset(self):
return Articles.objects.filter(JOURNAL_ID=self.kwargs['journal_id'])
You can read more about django’s dynamic filtering in the docs.
I have a view called ListAEQ:
class ListAEQ(MixinView, ListView):
template_name = 'allometric/aeq_list.html'
model = Equation
def get_queryset(self):
return (Equation.objects.filter(owner=self.request.user))
I want to use the queryset from this view multiple times with different templates. For example I have a template that extends aeq_list.html, that replaces a block in the parent template with different content. How do I render this content using the same view but different templates, without having to create multiple views that have the same queryset and a different tempate_name. I believe there is a way to do this according to the principle "DRY"
For example, I would create a new view
class ListAEQindia(MixinView, ListView):
template_name = 'allometric/aeq_list_india.html'
model = Equation
def get_queryset(self):
return (Equation.objects.filter(owner=self.request.user))
You don't say how you're determining which template is to be rendered. But presuming it's based on a parameter from the URL, you can define the get_template_namesmethod in your view.
That method can access self.kwargs and self.request etc and then return a list containing the name of the template to use. (Note that it does have to be a list, even if the list only contains one item.)
I am trying to understand Django's class based views (very new to it), especially, ListView. I am struggling to understand where the "business logic should go". Say for example, I have the following class:
#views.py
class DisplayListView(ListView):
model = Cars
template_name = "searchres_list.html"
paginate_by = '5'
context_object_name = "titles"
def get_context_data(self, **kwargs):
context = super(SearchDisplayListView, self).get_context_data(**kwargs)
# custom logic whoch spits out "now". in this example [1 -->10]
context['now'] = [1,2,3,4,5,6,7,8,9,10]
return context
It works fine and I am able to look the [1 --> 10] on my template. However, when I look at the methods available under ListView I see that I could probably include my logic in get_queryset method. So, something like:
def get_queryset(self):
# Fetch the queryset from the parent get_queryset
queryset = super(SearchDisplayListView, self).get_queryset()
# custom logic whoch spits out "now". in this example [1 -->10]
queryset = [1,2,3,4,5,6,7,8,9,10]
return queryset
So, my rather (stupid) question is (or have I got this all completely wrong!), where should the business logic ideally go:
def get_context_data
def get_queryset
Thanks for your time.
Probably the best answer to such a subjective question will be: it depends.
My personal algorithm for dealing with the situations like this is the following:
if you need to add something to the context that will be passed to the template, then you don't have a choice actually, because in get_queryset method you can only modify the queryset for your ListView. So I use get_context_data in this case.
but if you're going to perform some dynamic queryset modifications, let's say your view can operate on similar model classes and the actual class is determined by the arguments passed into the view, then probably you need to overwrite get_queryset method.
Hope I gave you some insights on the topic :)
I would like to solve the following situation.
I have a side panel containing information of the active user. For this an instance of UserInfo model needs to be passed to the views.
Additionally, I would like to pass a number of other model instances to the pages (eg. Purchases, Favourites, etc.).
I know this is pretty easy to do by overriding the get_context_data.
def get_context_data(self, **kwargs):
kwargs['purchases'] = Purchases.objects.get(id=1)
kwargs['favourites'] = Favourites.objects.get(id=1)
.... etc
return super(UploadFileView, self).get_context_data(**kwargs)
So my question is - what would be the best/most appropriate CBV to use for this?
This isn't quite a DetailView as you have multiple objects, but it isn't a ListView either, nor does it look like a FormView or its children.
Since you gain nothing from those, a simple TemplateView is probably the way to go.
If you are querying the same UserInfo, Purchases, Favorites, etc in multiple views, create a Mixin that you can re-use.
class CommonUserInfoMixin (object):
def get_context_data(self, **kwargs):
context = super(OrgContextMixin, self).get_context_data(**kwargs)
... # Add more to context object
Then you can use this in your normal List, Detail, Update, etc CBV's
class ItemList(CommonUserInfoMixin, ListView):
....