Multiple models in UpdateView - django

Is it possible to pass multiple models into the UpdateView?
Something like:
models = (FirstModel, SecondModel)

Not via the models attribute for UpdateView.
But what you can do is either utilize extra_context or override the get_context_data() and add the models there.
An example of one such override would be:
class TaffyUpdateView(UpdateView):
def get_context_data(self, **kwargs):
context = super(TaffyUpdateView, self).get_context_data(**kwargs)
context['second_model'] = SecondModel.objects.get(id=1) #whatever you would like
return context

Related

GenericViews - and pushing model name in URL

using Django Generic CreateView - is it possible for me to pass a value into the CreateView from the URL call, that defines which model/table to base the view on?
I did try get_context_data but believe that is not the solution, as I think it only pushes it to the rendered template.
You will see from my scripts - I am pushing the value 'syslog_policy' - i would like the view to set the model variable to be, what-ever value I push from the URL.
The reason for this, I have some pages/models that are similar - so rather than create multiple html pages and views - I wouldn't need to if I could get this to work.
URL Call
<li>Update/Delete Policies</li>
urls.py
path('HardenTemplateCreate/<str:element>', HardenTemplateCreate.as_view(success_url="/security_tooling/add_success") ,name="HardenTemplateCreate")
views.py
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['element']= self.kwargs['element']
print(context['element'])
return context
model = !!<NEED VALUE SUPPLIED IN URL HERE>!!
fields = ['name','template']
template_name = 'security_app/add.html'```
This would assume that all the models we are working with here belong to the same app (main) - if not, you also need to pass that to form kwargs and handle accordingly. In forms.py:
from django.apps import apps
from django.forms.models import fields_for_model
class VariableModelForm(forms.Form):
def __init__(self, *args, **kwargs):
model_name = kwargs.pop('model_name', None)
model = apps.get_model(app_label='main', model_name=model_name)
model_fields = fields_for_model(model)
super(VariableForm, self).__init__(*args, **kwargs)
for field in model_fields:
self.fields[field] = model_fields[field]
In your CreateView:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form']= VariableModelForm(model_name=self.kwargs['modelname'])
return context
You grab the model name from the url kwargs, pass it to your form as an extra positional argument, and use it in your form's init method to set up the fields.

how to merge x views in one template

I'm trying to use two views list(post_list and classification_list) in a template called blogpage. here's what I've done to solve the problem, however it didn't work:
class GenViewList(ListView):
model = Posting,Classification
template_name = 'Blog/blogpage.html'
def get_context_data(self, **kwargs):
context=super(BlogViewList,self).get_context_data(**kwargs)
context['latest_post_list']=Posting.objects.filter().order_by('-id')[:30]
context['classification_list']=Classification.objects.all().order_by('id')
return context
Any help will be appreciated!
You can just make it a TemplateView
from django.views.generic import TemplateView
class GenViewList(TemplateView):
template_name = 'Blog/blogpage.html'
def get_context_data(self, **kwargs):
context=super(BlogViewList,self).get_context_data(**kwargs)
context['latest_post_list']=Posting.objects.filter().order_by('-id')[:30]
context['classification_list']=Classification.objects.all().order_by('id')
return context
ListView doesn't work with 2 different models. You can provide your get_queryset, but in the way you construct your get_context seems you need something different like TemplateView

#method_decorator with login_required and permission_required

I'm using Class-based views where I would like to ensure that each view can be access by logged-in users and by one type of the users only (there are two groups of users - each group has different permissions).
I'm implementing this according to the docs with persmissions (I'm using Django 1.7.7) https://docs.djangoproject.com/en/1.7/topics/class-based-views/intro/#decorating-the-class, however using two parameters raises an error " method_decorator() takes exactly 1 argument (2 given)" .
Hence - how to do it such that those two factors(login and permissions) will be verified in a class-based view?
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(login_required, permission_required('patient.session.can_add_patient'))
def dispatch(self, *args, **kwargs):
return super(PatientCreate, self).dispatch(*args, **kwargs)
Thanks!
In your case, permission_required will redirect to the login page for users that are not logged in, so you don't need to use login_required at all.
#method_decorator(permission_required('patient.session.can_add_patient')
def dispatch(self, *args, **kwargs):
...
If you really did need to use multiple decorators, then you can use a list in Django 1.9+
decorators = [other_decorator, permission_required('patient.session.can_add_patient')]
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(decorators)
def dispatch(self, *args, **kwargs):
...
You can also shorten the code by decorating the class itself:
#method_decorator(decorators, name="dispatch")
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
On Django 1.8 and earlier, you can't pass a list to method_decorator or decorate the class, so you have to stack the decorators
class PatientCreate(CreateView):
model = Patient
fields = '__all__'
#method_decorator(other_decorator)
#method_decorator(permission_required('patient.session.can_add_patient'))
def dispatch(self, *args, **kwargs):
...
The decorators will process the request in the order they are passed to method_decorator. So for the examples above, other_decorator will run before permission_required.

Combine CreateView with DetailView in Django 1.5

In my app I need to create products in a shop. So I have a model Shop and a model Product. I can see details about my shop in a DetailView ShopDetail. Now I need a CreateView in order to create products, but the url should be /shops/shop-id/products/create/, so I create products inside the shop. I guess it's something like
class ProductCreate(SingleObjectMixin, CreateView):
model = Product
def get_object(self, queryset=None):
return Shop.objects.get(id = self.kwargs['shop_id'])
Am I on the right track? :-D
No, you're not on the right track: the object returned by get_object should be a instance of the model; in fact, if you override get_object the model attribute becomes irrelevant.
There are a few approaches to this problem, but I would myself probably got for a single DetailView (with the Shop details), and add a form for Product to the template via the get_context_data method. The form's action attribute would, instead of being empty, point to the url to a separate CreateView which would handle the Product creation.
Alternatively you could simply display the Shop details through the get_context_data, which is simpler but mixes concerns (as the DetailView for shop is defined as a CreateView for Product).
I think you need:
from django.shortcuts import get_object_or_404
class ProductCreate(CreateView):
"""Creates a Product for a Shop."""
model = Product
def form_valid(self, form):
"""Associate the Shop with the new Product before saving."""
form.instance.shop = self.shop
return super(CustomCreateView, self).form_valid(form)
def dispatch(self, *args, **kwargs):
"""Ensure the Shop exists before creating a new Product."""
self.shop = get_object_or_404(Shop, pk=kwargs['shop_id'])
return super(ProductCreate, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
"""Add current shop to the context, so we can show it on the page."""
context = super(ProductCreate, self).get_context_data(**kwargs)
context['shop'] = self.shop
return context
I hope it helps! :) You may wish to have a look at what the super-methods do.
(Disclaimer: Shameless self promotion.)

Extending generic view classes for common get_context_data

I constantly see myself having to add the same extra variable to the context of many of my views.
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(MyListView, self).get_context_data(**kwargs)
# Add in the house
context['house'] = self.get_object().house
return context
As I don't like repeating myself, I thought I could create a new class extending the view, and then I could base all my views on the new extended view class. The thing is, there are 4 classes of views I use: CreateView, UpdateView, ListView, and DeleteView. Do I really have to create a new class for each one of them?
Isn't there something like a Django "base" view class? Maybe a smarter way to do this?
Create a Mixin:
from django.views.generic.base import ContextMixin
class HouseMixin(ContextMixin):
def get_house(self):
# Get the house somehow
return house
def get_context_data(self, **kwargs):
ctx = super(HouseMixin, self).get_context_data(**kwargs)
ctx['house'] = self.get_house()
return ctx
Then in your other classes you'd use multiple inheritance:
class HouseEditView(HouseMixin, UpdateView):
pass
class HouseListView(HouseMixin, ListView):
pass
and so on, then all these views will have house in the context.