I have a url set up dynamically for a project using:
path('project/<int:pk>', ProjectView.as_view(), name='project')
How can I make this so I can use two parameters, something like this:
path('project/<int:pk>/<int:category', ProjectView.as_view(), name='project')
So I need to set up links to each category, so the user will see only the updates from Project A category 1 of 7.
If ProductView is a DetailView, you need to alter the get_queryset a bit, like:
from django.views.generic.detail import DetailView
class ProductView(DetailView):
model = Product
template = 'some_template.html'
def get_queryset(self):
return super().get_queryset().filter(
category__id=self.kwargs['category']
)
Here we thus will filter the queryset first by 'category', and the boilerplate code of the DetailView will then filter by the primary key pk.
In your templates, you can generate urls, with:
{% url 'project' pk=some_pk category=some_category_id %}
or if you for example redirect:
return redirect('project', pk=some_pk, category=some_category_id)
Related
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
admin.py
#admin.register(StudentsEnrollmentRecord)
class StudentsEnrollmentRecord(admin.ModelAdmin):
list_display = ('Student_Users', 'School_Year', 'Report')
ordering = ('Education_Levels',)
list_filter = ('Student_Users',)
I just want that to add the html link in the adminsite then if the admin click the "report" it will filter what studentenrollmentrecord selected to html file
You need to create a method to calculate this field for each entry in the list, as follows:
from django.utils.html import format_html
#admin.register(StudentsEnrollmentRecord)
class StudentsEnrollmentRecord(admin.ModelAdmin):
list_display = ('Student_Users', 'School_Year', 'report')
...
def report(self, obj):
return format_html(f'<a href={pass_link_here}>Report</a>')
Those kinds of methods take actual objects as a parameter, you can use it to create different links for different objects. obj.field_name
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.
I am trying to make this template call a function that has worked in the past with a many-to-many relationship. This time it is a many-to-one.
the detail.html template
{{ icecreamflavor.name.all }}
The view:
from django.shortcuts import get_object_or_404, render
from django.views import generic
from .models import IceCream, IceCreamFlavor
# Create your views here.
class IndexView(generic.ListView):
template_name = 'ice_cream/index.html'
context_object_name = 'ice_cream_list'
def get_queryset(self):
return IceCream.objects.order_by('brand')
class DetailView(generic.DetailView):
model = IceCreamFlavor
template_name = 'ice_cream/detail.html'
2 Models:
class IceCream(models.Model):
ice_cream_id = models.AutoField(primary_key=True)
brand = models.CharField(max_length=200)
def __unicode__(self):
return self.brand
class IceCreamFlavor(models.Model):
ice_cream_flavor_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
rating = models.DecimalField(max_digits=3, decimal_places=1)
brand = models.ForeignKey(IceCream)
def __unicode__(self):
return self.name
This is a many to one relationship, and I believe that in my view I have something messed up at the model for the DetailView. I want to use the objects.all() method to simply call all the flavors of the ice cream in the detail view.
Nothing comes up. The documentation in the tutorial has me confounded a bit. The IndexView works and show up all the brands; just need DetailView to bring up all the flavors; I will format later. For the record, {{ icecreamflavor }} in the detail.html template does bring up only one flavor and not all of them. I believe it is something about instances..
Index View
First of all, when you are using Django Generic List View the object list in the template is called object_list. Check the above link to see django documentation.
So to show all the IceCream names in the Index template you should do:
{% for item in object_list %}
{{item.brand}}
{% endfor %}
To access the IceCreamFlavors of an IceCream you should do like this:
{% for item in object_list %}
{{item.icecreamflavor_set.all}}
{% endfor %}
{{item.icecreamflavor_set.all}} <-- Check that I'm using icereamflavor_set
When the foreign key is from IceCreamFlavor to IceCream, and you want to access from IceCream to IceCreamFlavor you need to add _set to the model's name.
Detail View
The second view is a Django Generic DetailView so it's completely normal behaviour that the view only send one item to the template.
If you want to pass all the items you need to change the view from DetailView to ListView.
I recommend you to check both documentations about ListView and DetailView to understand better what's happening there.
Also I recommend you to check Django: Backward relationships
Suppose I have the following models.
Class Blog:
pass
class Entry:
models.ForeignKey(Blog)
I want to do something like this
filter & sort blog by some criteria
get related entries for page 2
Can I do something better than below?
blog_ids = Blog.objects.filter(q).order_by(order)
entry_qs = Entry.objects.filter(id__in=blog_ids)
paginator = Paginator(entry_qs)
entries = paginator.page(2)
I think you need to use order_by on the entries rather than the blog object. An easier way of implmenting sorting and pagination of blog entries would be using a class based view like ListView. For eg. in your views.py:
from django.views.generic import ListView
from .models import Entry
class BlogView(ListView):
queryset = Entry.objects.order_by("-date")
paginate_by = 10
You will need to mention it in the urls.py:
url(r'^$', BlogView.as_view(), name='home'),
Now, if you use a url like /page=1 it will show the second page of entries (passed as object_list)