New Urls patterns and multiple variables with the same name - django

I'm using Django 2.0 type of urls, and I have urls with multiple variables in them, with the same name. I'm using also ClassBasedView
path('/companies/<int:pk>/products/<int:pk>/', AccountCompanyProductDetailView.as_view()
I'm using pk because is the primary key and CBV will know how to use it (similar for other Model fields).
If I use other names, CBV will not know what to search.
In a CBV how can I get the parameters and know which is which. ?
How Django knows pk from which Model I need in each position ?

Django does not know how to handle this. You need to rename your parameters and access them in your CBV.
This could look like the following:
urls.py:
path('/companies/<int:pk1>/products/<int:pk2>/', AccountCompanyProductDetailView.as_view())
views.py:
class AccountCompanyProductDetailView(DetailView):
model = Product
def get_object(self):
pk1 = self.kwargs['pk1']
pk2 = self.kwargs['pk2']
company = get_object_or_404(Company, pk=pk1)
product = get_object_or_404(Product, pk=pk2)
return product
You would need to do this in other views too. Override the according methods like get_queryset. Access the kwargs as shown above.

Related

Django: from request.GET to QuerySet

I know how to create a html form with Django and I know how to access the values in request.GET.
I know how to use the ORM: MyModel.objects.filter(...)
Now I a missing how get from request.GET to a matching QuerySet
I could not find this in the Django docs.
Example:
class MyModel(models.Model):
name=models.CharField(max_length=1024)
if request.GET contains name=foo I would like to have a filter like MyModel.objects.filter(name__icontains=request.GET['name']).
I could code this on my own, but I think this is re-inventing the wheel. It feels too complicated to me.
Above is just one example. I prefer a solution which is more configuring than coding.
We can here construct a Q object:
from django.db.models import Q
MyModel.objects.filter(
Q([('{}__icontains'.format(k), v) for k, vs in request.GET.lists() for v in vs])
)
This will work as well given a certain key occurs multiple times in the querystring. Note that the fields you query over should support an __icontains [Django-doc] lookup.
Furthermore it might be unsafe to allow that, since for example a hacker could try to use user__password to make guesses on the (hashed) password of a user.
You should do it this way
filters = {}
for param, value in request.GET.items():
filters['{}_icontains'.format(param)] = value
queryset = MyModel.objects.filter(**filters)
Reference https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.QueryDict.items
You can use django-tables2 and django-filter
Explained here:
Django-filter can be used for generating interfaces similar to
the Django admin’s list_filter interface. It has an API very similar
to Django’s ModelForms. For example, if you had a Product model you
could have a filterset for it with the code:
import django_filters
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = ['name', 'price', 'manufacturer']
And then in your view you could do:
def product_list(request):
filter = ProductFilter(request.GET, queryset=Product.objects.all())
return render(request, 'my_app/template.html', {'filter': filter})
How to use django-filter in django-tables2, see django-filter in django-table2

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.

Django - force pk_url_kwarg to query other model instances

Consider the following code:
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Is there any way that I can force pk_url_kwarg to query
& return other model instances defined in models.py (like model = other_model), while having my get_object() method to return objects in model = BHA_overall? What CBV should I use (I think UpdateView is not a good choice in this case)?
++ I'm trying to make a page that allows users to manage information about the product they use. So, ultimately I will implement forms, and the user input needs to be saved in DB
++ I need pk_url_kwarg = 'pk_alt' to query other models and generate url. But I still need get_object() method to return objects in model = BHA_overall to generate form fields on the user side.
From my understanding you need a django form generated from BHA_overall, but the data should be saved to AnotherModel right?
I will propose 2 solutions to this problem, Choose what best fits you.
Multiple views:
Have multiple views for the task, What I mean is create a view which creates the form for the frontend using BHA_overall, you can create both Create and Update view this way and update view's initial could be overwritten so form will have expected value when editing. And now post the data to another view which handles the post data. This view can have your AnotherModel doing its thing.
Using Django Form:
If you dont like having multiple views, You can keep things simple by creating a form yourself. Create a DjangoForm with the same fields you want to show to the user and use it in to create your own views, Now you wont need BHA_overall and use your AnotherModel to save datal.

limit choices in drop downs in django rest browsable api

Is there a way to limit what fields are populated (such as in dropdown selectors or list selectors) in the DRF browsable API?
Below is an image example of how DRF is suggesting choices of "projects" to the user that he should select. However, the logged in user may or may not have access to these projects, so I'd like to get control over what shows up here! It seems that the default behavior is to show all related objects.
It would be really useful if there was a way to link the objects populated in these fields to be set according to a get_queryset() function.
This page seems to hint that it might be possible, I just can't find an example of how to do it: http://www.django-rest-framework.org/api-guide/filtering/
You can define a new field based on some of the serializers.RelatedField-ish classes, and use the request in the context to redefine what the method get_queryset returns.
An example that might work for you:
class AuthorRelatedField(serializers.HyperlinkedRelatedField):
def get_queryset(self):
if 'request' not in self.context:
return Author.objects.none()
request = self.context['request']
return Author.objects.filter(user__pk=request.user.pk)
class BookSerializer(serializers.HyperlinkedModelSerializer):
author = AuthorRelatedField(view_name='author-detail')
Check the templates in DRF/temapltes/rest_framework/inline/select.html. The select object in DRF uses the iter_options method of the field to limit the options in the select tag, which in turn uses the queryset from get_queryset.
This would efectively limit the options of the select tag used in the browsable API. You could filter as well in the get_choices method if you want to preserve the queryset (check the code in DRF/relations.py). I am unsure if this impacts the Admin.
I don't fully understand what do you want but try queryset filter's __in function if you need to show only exact values:
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
to_show = [ "user1", "user2", "user3"]
queryset = Purchase.objects.all()
username = self.request.query_params.get('username', None)
if username is not None:
queryset = queryset.filter(purchaser__username__in=username)
return queryset
you can add your values to to_show list and if queryset element equals one of them then it will be shown.
Also if you want to show only some fields of model you need to edit your Serializer's fields parameter:
class PurchaseList(serializers.ModelSerializer):
class Meta:
model = Purchase
fields = ('id', 'field1', 'field2', ...)

How to generate urls with Django based on table variable

I have a blog where i would like to construct my URLs in following fashion: blah.com/blog/this-is-my-first-blog-post where this-is-my-first-blog-post is the title of one specific blog post.
Is there a way to generate these types of urls based on the title column in my table with blogposts?
You'll be better off storing the URL key as a SlugField. You can determine that value by using slugify.
Then your code for the url would be something like:
url(r'^(?P<slug>[-\w]+)/$','example_view'),
And you can use get_object_or_404:
def example_view(request, slug):
instance = get_object_or_404(Model, slug=slug)
Or you can use a DetailView CBV as shown in the docs.
There are a couple of options:
In django admin, you can make the field pre-populate on editing.
prepopulated_fields = {'slug': ('title',), }
This needs to be put in the admin.py file.
In case you want to do it outside of admin, you can use a pre-save signal and use slugify as mentioned above by #schillingt above.