Django template inheritance: repetitive views - django

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.)

Related

Django user access control

For example, I have one model Post.
class Post(models.Model):
task = models.ForeignKey("User")
I want users can only access posts created by themselves but have too many views which list some posts to rewrite queryset in each view.
So is there any graceful way to control user access? Save and get current user in thread and implement a new Manager class to Post is a way, but I don't know why this is not recommended.
Assuming you are using generic ListView(Django-doc). You can override the get_queryset(...)(Django-doc) method of view as below,
class PostListView(ListView):
def get_queryset(self):
return Post.objects.filter(task=self.request.user)
Generic Solution
First you need to create a mixin class,
class GenericLoggedInMixin:
user_field_name = None
def get_queryset(self):
queryset = super().get_queryset()
if self.user_field_name:
return queryset.filter(**{self.user_field_name: self.request.user})
return queryset
and inherit the same in your view as,
class PostListView(GenericLoggedInMixin, ListView):
user_field_name = 'task'

Accessing model/class information within context processor

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

Passing URL variables to a class based view

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.

WTForm with more than one model object

I have a page that should create an instance of two distinct models at the same time, ie, when the same form is submitted.
I created a form using wtforms_alchemy but this covers only one model, not both:
class RoutineForm(ModelForm):
class Meta:
model = Routine
only = [u'name', u'comment']
field_args = {u'comment': {'widget': TextArea()}, }
I know I could create a form that does not inherit from the model and includes all the fields I need but for the sake of keeping things DRY how can I have an instance of ModelForm use two models?
You can use multiple forms at the same time. Give each a prefix to make sure the field names don't overlap in HTML. Validate both forms. Other than having two form objects, everything else is normal about the view and template.
class RoutineForm(ModelForm):
...
class EventForm(ModelForm):
...
#app.route('/make_event', methods=['GET', 'POST'])
def make_event():
routine_form = RoutineForm(prefix='routine')
event_form = EventForm(prefix='event')
if request.method == 'POST' and routine_form.validate() and event_form.validate():
...
return render_template('make_event.html', routine_form=routine_form, event_form=event_form)

Rest Framework Class Based Views with partial assignments

I'm following the tutorial laid out here to create generic class based views for my API - however, I have run into a small problem. I would like to update the model behind the view partially. I used to be able to do this by using the partial property when I created the serializer. However, it seems that once I start using generic class based views I lose the ability to set whether or not I can allow partial updates to the model. How can I override the partial property of a ModelSerializer? My code is quite simple:
class DejavuUserDetail(generics.RetrieveUpdateAPIView):
'''
Get a user or update a user
'''
lookup_field = "email"
queryset = DejavuUser.objects.all()
serializer_class = UserSerializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = DejavuUser
partial = True
def restore_object(self, attrs, instance=None):
"""
Given a dictionary of deserialized field values, either update
an existing model instance, or create a new model instance.
"""
if instance is not None:
#set the required fields and return the instance
I'm trying to access the API via PUT
For partial updates use PATCH.
Also note that partial isn't an option on the serializer metaclass, but instead set on instantiating the serializer.