How to pass additional arguments to class view in django? - python

def edit_course(request,course_id):
course=Courses.objects.get(id=course_id)
return render(request,"hod_template/edit_course_template.html",{"course":course,"id":course_id})
The code above shows a view in django that is defined as a function. I want to recreate the same view as a class based view in django but I couldn't pass in an additional argument such as course_id into the class based view as it shows an error. Can someone tell me how to recreate that above function into a class based view ?

You can make a DetailView with 'course_id' as pk_url_kwarg. An equivalent DetailView is:
from django.views.generic import DetailView
class EditCourseView(DetailView):
model = Courses
pk_url_kwarg = 'course_id'
context_object_name = 'course'
template_name = 'hod_template/edit_course_template.html'
Note: normally a Django model is given a singular name, so Course instead of Courses.

Possible duplicate of URL-parameters and logic in django...
You can access your course_id via
self.kwargs['course_id']
Use it inside class-based view functions

Related

Limiting choices in foreign key dropdown in Django using Generic Views ( CreateView )

I've two models:
First one:
class A(models.Model):
a_user = models.ForeignKey(User, unique=False, on_delete=models.CASCADE)
a_title = models.CharField("A title", max_length=500)
Second one:
class B(models.Model):
b_a = models.ForeignKey(A, verbose_name=('A'), unique=False, on_delete=models.CASCADE)
b_details = models.TextField()
Now, I'm using CreateView to create form for Value filling :
class B_Create(CreateView):
model = B
fields = ['b_a','b_details']
Then using this to render these field in templates.
Now, my problem is, while giving the field b_a ( which is the dropdown ), it list downs all the values of model A, but the need is to list only the values of model A which belongs to the particular logged in user, in the dropdown.
I've seen all the answers, but still not able to solve the problem.
The things I've tried:
limit_choices_to in models : Not able to pass the value of A in the limit_choices
form_valid : Don't have the model A in the CreateView, as only B is reffered model in B_Create
passing primary key of A in templates via url : Then there is no instance of A in the template so can't access. Also, don't want to handle it in templates.
I'm new to Django and still learning, so don't know to override admin form.
Please suggest the implemented way, if possible to the problem. I've researched and tried most of the similar questions with no result for my particular problem. I feel like, this is a dumb question to ask, but I'm stuck here, so need help.
Thanks..
(Please feel free to suggest corrections.)
You have access to self.request.user in the form_valid of the view. But in order to limit the choices in the form you have to customize the form before it is served initially. You best override the view's get_form and set the form field's queryset:
class B_Create(CreateView):
model = B
fields = ['b_a','b_details']
def get_form(self, *args, **kwargs):
form = super(B_Create, self).get_form(*args, **kwargs)
form.fields['b_a'].queryset = self.request.user.a_set.all()
# form.fields['b_a'].queryset = A.objects.filter(a_user=self.request.user)
return form
Generally, there are three places where you can influence the choices of a ModelChoiceField:
If the choices need no runtime knowledge of your data, user, or form instance, and are the same in every context where a modelform might be used, you can set limit_choices_to on the ForeignKey field itself; as module level code, this is evaluated once at module import time. The according query will be built and executed every time a form is rendered.
If the choices need no runtime knowledge, but might be different in different forms, you can use custom ModelForms and set the queryset in the field definition of the respective form field.
If the queryset needs any runtime information, you can either override the __init__ of a custom form and pass it any information it needs to set the field's queryset or you just modify the queryset on the form after it is created which often is a quicker fix and django's default views provide nice hooks to do that (see the code above).
The #schwobaseggl answer is excellent.
Here is a Python 3 version. I needed to limit the projects dropdown input based on the logged-in user.
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
template_name = 'brand/product-create.html'
fields = '__all__'
def get_form(self, form_class=None):
form = super().get_form(form_class=None)
form.fields['project'].queryset = form.fields['project'].queryset.filter(owner_id=self.request.user.id)
return form

Django 1.9 get kwargs in class based view

I was wondering if there is a way to get the kwargs directly in a class based view. I know this can be done in functions inside the class, but I'm having problems when I try this:
views.py
class EmployeesUpdateStudies(UpdateView):
form_class = form_ES
model = EmployeePersonal
template_name = 'employeesControll/employees_studies_update_form.html'
success_url = reverse('employee-details', kwargs={'pk': kwargs.get('pk')})
My url is the following
url(r'^employees/detalles/(?P<pk>[0-9]+)/$', login_required(views.EmployeeDetails.as_view()), name='employee-details')
Alasdair's answer solves your problem. You can however define a get_absolute_url method for your EmployeePersonal model which will act as the success_url for your view:
You don’t even need to provide a success_url for CreateView or
UpdateView - they will use get_absolute_url() on the model object
if available.
You'll use self.id in the get_absolute_url method for the model objects primary key.
Reference:
Model Forms
You can't use kwargs in success_url, because when Django loads the class when the server starts, it doesn't have access to the request. Override the get_success_url method instead.
def get_success_url(self)
return reverse('employee-details', kwargs={'pk': self.kwargs['pk']})

Filtering Objects in Class based view Django using Query parameters?

I am using Class-based Generic views Listview for listing all objects.
My views.py:
class PostsList(ListView):
model = Post
template_name = "index.html"
My Urls.py:
urlpatterns = [
url(r'^$',PostsList.as_view(), name = "home"),
]
This gives me a list of all the posts. Now I want to filter/sort posts based on certain fields of Post Model, say price. Do I need to write this myself? If yes Which method of PostsLists class do I override ? def get, def get_context ?
I see the get method for Listview defined as below. In it can I pass URL query-parameters as **kwargs directly or I have to overwrite the below method in my class.
def get(self, request, *args, **kwargs):
....
You can override the get_queryset method:
Keep a mapping of all the parameters that you can get in the url kwargs.
def get_queryset(self):
queryset = Post.objects.all()
if self.request.GET.get('price'):
queryset = queryset.filter(price=self.request.GET.get('price'))
return queryset
When using Django's class based views, avoid overriding get() or post() if possible. These methods do a lot, and if you override them, you may have to duplicate a lot of the built in functionality. There are normally more specific methods that you can override.
In your case, you can filter the queryset dynamically with the get_queryset method. You can access GET parameters with self.request.GET. For example:
class PostsList(ListView):
model = Post
def get_queryset(self):
"""Filter by price if it is provided in GET parameters"""
queryset = super(PostsList, self).get_queryset()
if 'price' in self.request.GET:
queryset = queryset.filter(price=self.request.GET['price'])
return queryset
If your url captures arguments, you can access them with self.args (positional) and self.kwargs (name based).
See the docs on dynamic filtering for more info.

Access model name in Django CreateView from template

I'm using the generic CRUD views in Django 1.6, e.g.:
class KanriCreateView(CreateView):
template_name = 'generic_form.html'
class KanriUpdateView(UpdateView):
template_name = 'generic_form.html'
etc.
N.B. These are classes used as a base class which I subclass inside views.py files throughout the project.
In order to keep DRY I'm writing a generic form template for all create/update views.
For update views, I have access to object in the template, which is the instance I am updating. I then use object.__class__.__name__ (via a custom filter) to get the name of the class (so I can have automatically generated custom buttons like "Add User", "Add Role".etc so the forms look less...generic.
Of course, when I'm using my template in CreateView, object does not exist (as it has not been created), so my custom buttons.etc do not work, and I get a VariableDoesNotExist exception.
Does Django provide the class somewhere so I can use it in the template?
The name of your first view should be different, e.g. KanriCreateView
It might help you to get the name of the view class: {{ view.class.name }}
If you have access to the view class (which is provided by default by the ContextDataMixin) you can access the model attribute of the view class and get the name of the model: {{ view.model.__name__ }}
Cheers
If you're using a ModelForm for your CreateView, this doesn't quite work. This is because you're not specifying
model = MyModel
but instead you're specifying
form_class = MyModelForm
so what you can do instead is
from django.contrib.admin.utils import model_ngettext
model_ngettext(self.form_class._meta.model, 1)
I propose a solution updated for Django 2 and 3: retrieve the model verbose name from the ModelForm associated to the CreateView.
class YourCreateView(CreateView):
form_class = YourModelForm
def get_context_data(self, **kwargs):
"""Add the models verbose name to the context dictionary."""
kwargs.update({
"verbose_name": self.form_class._meta.model._meta.verbose_name,})
return super().get_context_data(**kwargs)
Now you can use {{ verbose_name }} inside your template.
Please, remark the double _meta in the code snippet above: the first is meant to access the model from the ModelForm, while the second accesses the verbose name of the Model.
As with internationalization, remark that if your model uses ugettext as shown below, then the verbose name will automatically be translated in the template.
from django.utils.translation import ugettext_lazy as _
class MyModel(models.Model):
class Meta:
verbose_name = _("your verbose name")

Django ModelForm with User data in Generic View

I have a model with a foreign key to group (the other fields don't matter):
class Project(models.Model) :
group = models.ForeignKey(Group)
...
I have a model form for this model:
class AddProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ["group","another"]
In my urls, I am using this in a generic view:
(r'^$', create_object, {'form_class':AddProjectForm, 'template_name':"form.html", 'login_required':True, 'extra_context':{'title':'Add a Project'}}),
That all works, but I want to have the group field display only the groups that the current user belongs to, not all of the groups available. I'd normally do this by passing in the user to the model form and overriding init if I wasn't in a generic view. Is there any way to do this with the generic view or do I need to go with a regular view to pass in that value?
This is gonna look dirty, since the generic view instantiates the form_class with no parameters. If you really want to use the generic_view you're gonna have to generate the class dynamically :S
def FormForUser(user):
class TmpClass(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(TmpClass, self).__init__(*args, **kwargs)
self.fields['group'].queryset = user.group_set.all()
class Meta:
model = Project
fields = ['group', 'another']
Then wrap the create object view
#login_required # Only logged users right?
def create_project(request):
user = request.user
form_class = FormForUser(user)
return create_object(request, form_class=form_class, ..... )
My recommendation is to write your own view, it will give you more control on the long term and it's a trivial view.
No, you'll need to make a regular view. As can be seen by looking at the source code for create_object(), there's no functionality to pass in extra parameters to the modelform (in django 1.2):
http://code.djangoproject.com/svn/django/branches/releases/1.2.X/django/views/generic/create_update.py

Categories