How to get user specific object using django generic class detailview? - django

How do I get the current user related object using django's generic class DetailView? Using function based views, I can obtain the object like this:
def foo(request):
friendProfile = get_object_or_404(request.user.profile.friends,username="admin")
What is the equivalent using detail view? I'm guessing it's something related to get_object or get_context_data but I can't fully understand the documents.
Thank you.

request is an attribute of class based views. To get the current user you should use self.request.user.
On a DetailView overload the get_queryset to edit the queryset used to get the object.
I don't know precisely how your friend model is defined but let's assume it has a foreign key pointing to your profile with a related_name set to friend_of. Your view could be:
class FriendProfileDetail(DetailView):
model = Friend
def get_queryset(self):
return self.model.objects.filter(friend_of=self.request.user.profile)

Related

Is it possible to edit the 'field' class variable in Django CBV using URL args?

I am trying to use a CreateView to create a profile object. Depending on the type of profile, though, I want to show a different set of fields. Right now I am passing the type of profile through the url.
How would I go about accessing this argument to filter the types of fields? I know how to do so in something like a get() function in a CBV, but not in the body of the class itself. I also do not know if there is an appropriate function to do this in and have not been able to find a solution in the docs.
Thank you in advance for any help, and I am sorry if this question has an obvious answer that I have missed.
You would need to override some method. Potential methods that can work are get_form_class, get_form, etc. Try overriding get_form:
class MyView(CreateView):
model = SomeModel
fields = None
def get_form(self):
self.fields = ['field1', 'field2'] # set this according to your conditions
# the keyword arguments passed to the view can be accessed by using self.kwargs.get('<key_name>')
return super().get_form()

Django detailview get_queryset and get_object

I am using Django detailview. initially, I used the URL pattern
url(r'^todo/details/(?P<pk>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),
my view is
class todoDetailView(DetailView):
model = models.todo
It worked fine.
In the second case, my URL is
url(r'^todo/details/(?P<id>[\d]+)', views.todoDetailView.as_view(), name='detail_todo'),
this time, I modified my view to
class todoDetailView(DetailView):
model = models.todo
# context_object_name = 'todo_detail'
def get_object(self, **kwargs):
print(kwargs)
return models.todo.objects.get(id=self.kwargs['id'])
It worked fine, I modified the second case to
class todoDetailView(DetailView):
model = models.todo
# context_object_name = 'todo_detail'
def get_queryset(self):
return models.todo.objects.get(id=self.kwargs['id'])
then I get an error,
Generic detail view todoDetailView must be called with either an object pk or a slug.
I know that there is no proper slug or pk provided. So, initially I added get_object() (it worked) but get_queryset() is not working. What is the difference in their working ??
And also if a user is getting details only based on the slug, I read on StackOverflow that
this can be used
slug_field = 'param_name'
slug_url_kwarg = 'param_name'
link - Generic detail view ProfileView must be called with either an object pk or a slug
Can anyone explain me actual working of get_object() and get_queryset() (also get_slug_field() if possible)
Along with the terms slug_field and slug_url_kwarg
Thanks in advance
get_object returns an object (an instance of your model), while get_queryset returns a QuerySet object mapping to a set of potentially multiple instances of your model. In the case of the DetailView (or in fact any class that inherits from the SingleObjectMixin, the purpose of the get_queryset is to restrict the set of objects from which you'll try to fetch your instance.
If you want to show details of an instance, you have to somehow tell Django how to fetch that instance. By default, as the error message indicates, Django calls the get_object method which looks for a pk or slug parameter in the URL. In your first example, where you had pk in the URL, Django managed to fetch your instance automatically, so everything worked fine. In your second example, you overrode the get_object method and manually used the id passed as parameter to fetch the object, which also worked. In the third example, however, you didn't provide a get_object method, so Django executed the default one. SingleObjectMixin's default get_object method didn't find either a pk or a slug, so it failed.
There are multiple ways to fix it:
1. Use pk in the URL
The simplest one is to simply use the code you provided in your first example. I don't know why you were unsatisfied with that, it is perfectly fine. If you're not happy, please explain why in more detail.
2. Override get_object
This is the second solution you provided. It is overkill because if you properly configured your view with the correct options (as you will see in the following alternatives), Django would take care of fetching the object for you.
3. Provide the pk_url_kwarg option
If you really want to use id in the URL for some reason, you can indicate that in your view by specifying the pk_url_kwarg option:
class todoDetailView(DetailView):
model = models.todo
pk_url_kwarg = 'id'
4. Provide the slug_field and slug_url_kwarg options [DON'T DO THIS]
This is a terrible solution because you are not really using a slug, but an id, but it should in theory work. You will basically "fool" Django into using the id field as if it was a slug. I am only mentioning it because you explicitly asked about these options in your question.
class todoDetailView(DetailView):
model = models.todo
slug_field = 'id'
slug_url_kwarg = 'id'
Regarding your get_queryset method: in your example, it doesn't even get to be executed, but in any case it is broken because it returns an individual object instead of a queryset (that's what objects.get does). My guess is you probably don't need a custom get_queryset method at all. This would be useful for example if you had a complex permission system in which different users can only access a different subset of todo objects, which I assume is not your case. Currently, if you provide this get_queryset method, even if everything else is configured properly, you will get an error. Probably an AttributeError saying that the queryset object has no attribute filter (because it will actually be a todo object and not a QuerySet object as Django expects).
The default get_object for DetailView tries to fetch the object using pk or slug from the URL. The simplest thing for you to do is to use (?P<pk>[\d]+) in the URL pattern.
When you override get_object, you are replacing this default behaviour, so you don't get any errors.
When you override get_queryset, Django first runs your get_queryset method an fetches the queryset. It then tries to fetch the object from that queryset using pk or slug, and you get an error because you are not using either of them.
The slug_field and slug_url_kwarg parameters are both defined in the docs. The slug_fields is the name of the field in the model used to fetch the item, and slug_url_kwarg is the name of the parameter in the URL pattern. In your case, you are fetching the object using the primary key (pk/id), so you shouldn't use either of these options.
For your URL pattern with (?P<id>[\d]+), you could use pk_url_kwarg = 'id'. That would tell Django to fetch the object using id from the URL. However, it's much simpler to use your first URL pattern with (?P<pk>[\d]+), then you don't have to override any of the methods/attributes above.
get_object() majorly uses with the generic views which takes pk or id
like: DetailView, UpdateView, DeleteView
where get_queryset() uses with the ListView where we are expecting more objects
Also, get_object() or self.get_object() uses pk as default lookup field or can use slug Field
a little peek at get_object()
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
# Next, try looking up by slug.
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{slug_field: slug})
I can't help you with what the error message explicitly means, but get_queryset is used in listviews by get multiple objects, while get_object is used to get a single object (ie DetailView).
If you have a pk you can use to get an object, you don't need to specify a slug field. Slug field are used to filter out objects when you don't have or can't show the primary key publicly. This gives a better explanation of a slug field.

Class Based Views UpdateView django different type of model

how can I change model in UpdateView for different type of users? I have Student and Teacher inherited from AbstractBaseUser, and I need edit for for them
class EditUser(UpdateView):
success_url = '/success/'
template_name = 'edit-profile.html'
model = Teacher (I need to choose this Teacher or Student)
I know about get_template_names(self) method or get_success_url(self), but can not find any get_model method.
I need somethng like:
def get_model_name(self):
if self.request.user.user_type == 'teacher':
return Teacher
if self.request.user.user_type == 'student':
return Studend
Thank you.
According to Django documentation:
model
The model that this view will display data for. Specifying model = Foo
is effectively the same as specifying queryset = Foo.objects.all(),
where objects stands for Foo’s default manager.
queryset
A QuerySet that represents the objects. If provided, the value of
queryset supersedes the value provided for model.
get_queryset()
Returns the queryset that will be used to retrieve the object that
this view will display. By default, get_queryset() returns the value
of the queryset attribute if it is set, otherwise it constructs a
QuerySet by calling the all() method on the model attribute’s default
manager.
So, all you need is redefine get_queryset method

Overriding get_queryset in Django UpdateView. Why is it asking for <pk> or <slug> when I am returning the right queryset?

Here is my view.
class ModelxUpdateView(LoginRequiredMixin, UpdateView):
model = Modelx
template_name='template.html'
form_class = ModelxFormSet
def get_queryset(self):
# query_set = super(ModelxUpdateView, self).get_queryset().filter(user=self.request.user)
query_set = Modelx.objects.filter(user=self.request.user)
return query_set
The error that it's throwing is
AttributeError: Generic detail view ModelxUpdateView must be called with either an >object pk or a slug.
Could someone also clarify if editing multiple models is allowed via UpdateView? I mean if I am going to return a queryset, it's going to update each of the objects in that queryset right?
Could someone also clarify if editing multiple models is allowed via
UpdateView? I mean if I am going to return a queryset, it's going to
update each of the objects in that queryset right?
No, UpdateView is for a single object only. It inherits from the SingleObjectMixin which is why it needs a primary key to be passed in; as this primary key is used in the get_object method.
To do multiple object updates, try the UpdatesWithInlines view from the django-extra-views app.

Django Class-Based Generic Views and ModelForms

Like much documentation on generic views in Django, I can't find docs that explicitly describe how to use the new Class-Based Generic Views with Django Forms.
How is it done?
What have you tried so far? The class based views are pretty new, and the docs don't have a lot of examples, so I think you're going to need to get your hands dirty and experiment!
If you want to update an existing object, then try using UpdateView. Look at the mixins it uses (e.g ModelFormMixin, SingleObjectMixin, FormMixin) to see which methods you can/have to override.
Good luck!
The easiest way to use model forms with class based views is to pass in the model and keep a slug / pk captured in url, in which case you will not need to write any view code.
url(r'^myurl/$', CreateView.as_view(model=mymodel))
#Creates a model form for model mymodel
url(r'^myurl/(?<pk>\w+)/$', UpdateView.as_view(model=mymodel))
#Creates a model form for model mymodel and updates the object having pk as specified in url
url(r'^myurl/(?<slug>\w+)/$', DeleteView.as_view(model=mymodel, slug_field="myfield"))
#Creates a model form for model mymodel and deletes the object denoted by mymodel.objects.get(my_field=slug)
You can also override methods to obtain more complex logic. You can also pass a queryset instead of a model object.
Another way is to create a modelform in forms.py and then pass form_class to the url as
url(r'^myurl/$', CreateView.as_view(form_class=myform))
This method allows you to define form functions as well as Meta attributes for the form.