A 'related content' list with DetailView in Django - python

Based on an "Article" model, I'm trying to display a "related content" list in the template by filtering its model field named "category". This "category" field has a ManyToMany relationship to another model named "Category".
It's looks like a very simple task but I can't figure out how to achieve my purpose. By now, a list could be displayed but seems nothing was filtered out.
Below is my DetailView class with a "get_context_data()" method which can product a template tag for displaying a list. Apparently the "F()" class is not the solution.
class ArticleDetail(generic.DetailView):
model = Article
template_name = 'article/detail.html'
def get_context_data(self, **kwargs):
context = super(ArticleDetail, self).get_context_data(**kwargs)
context_related = Article.objects.filter(F('category')).distinct()
context['related'] = context_related
return context
Besides, I also tried to filter with arguments like "category" and "category__exact=F('category')" but still failed.
And here are the models (simplified for question):
class Article(models.Model):
title = models.CharField(max_length=100)
content_text = models.TextField()
category = models.ManyToManyField('Category', blank=True)
def __unicode__(self):
return self.title
class Category(models.Model):
title = models.CharField(max_length=100, unique=True)
def __unicode__(self):
return self.title

No, that's not what F() is for at all.
You don't explain exactly what you do want though. I presume you're looking for other articles in the same categories as the current article. That's easy enough:
Article.objects.filter(category__in=self.object.categories.all())

Related

How get products from product table based on the category search in django restframework, How do inner join in django restframework

I am very much new in django restframework,I tried to create a search api.
I have 2 models designed
class Category(models.Model):
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Product(models.Model):
product_name = models.CharField(max_length=255)
product_Comments = models.CharField(max_length=255)
size = models.CharField(max_length=10, null=True)
product_Status = models.BooleanField(default=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.product_Description
I want to create a rest API, in which I can search the category and based on the search i want to list the product which related to that category. How can I do it.
My Views.py
class productList(generics.ListAPIView):
serializer_class = productSerializer
def get_queryset(self):
queryset = Product.objects.all()
search = self.request.query_params.get('search', None)
if search is not None:
queryset = queryset.filter(product_name__icontains=search)
my urls
path('product_search/',views.productList.as_view()),
You're almost there, you just need to add filtering.
Also, you almost never need to override the get() method of a generic view. Look for the method that let's you do the least amount of work. For any view, you will do most work in get_queryset and a view specific method:
List: view.list()
Create: view.create()
Retrieve: view.retrieve()
Update: view.perform_update()
Destroy: view.perform_destroy()
The rest can be customized by setting the correct class attributes. There are very few cases where it is necessary to override a view's "get" or "post" methods.
So, get_queryset is the place to modify the collection of objects that you are going to work on. This is perfect for filtering and thus search. A lot of work is already available in DRF and you can use it:
class productList(generics.ListAPIView):
serializer_class = productSerializer
def get_queryset(self):
queryset = Product.objects.all()
search = self.request.query_params.get('search', None)
if search is not None:
queryset = queryset.filter(category__name__icontains=search)
# don't forget to return the queryset
return queryset
For filtering the Products based on category name is,
Product.object.filter( category__name = 'category_name')

Reorganizing ManyToMany models into a different order

I have an setup similar to this.
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
books = models.ManyToManyField(Book)
def __unicode__(self):
return self.title
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author)
def __unicode__(self):
return self.title
Now in my admin panel I can select an author and add Books underneath that specific author. When I print out all the books for each author it prints them in the order that they were added in the admin panel.
Is it possible to modify the order in the admin panel so that when they print, they print in the order of the admin panel?
admin.py
#admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
pass
#admin.register(Book)
class BookAdmin(admin.ModelAdmin):
pass
You can add inside every model a class Meta with ordering atribute, to get items in a certain order.
class Book(models.Model):
...
class Meta:
ordering = ['author']
Maybe it helps you.
as i understand you use TabularInline for books if that's right so you just need to make simple function called get_queryset
def get_queryset(self, request):
return super(BookInlineAdmin, self).get_queryset(request).order_by('YOURFIELD')

Query a double foreign key and display in template

Models:
class Patient(models.Model):
patientID = models.CharField(max_length=200, unique=True, help_text='Insert PatientID')
birth_date = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
gender = models.CharField(max_length=200,choices=Gender_Choice, default='UNDEFINED')
class Examination(models.Model):
number_of_examination = models.IntegerField()
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date_of_examination = models.DateField(auto_now=False, auto_now_add=False, help_text='YYYY-MM-DD')
class GeneralData(models.Model):
examination = models.ForeignKey(Examination, on_delete=models.CASCADE)
height = models.FloatField(default='-', help_text= '[m] not [cm]! ')
weight = models.FloatField(default='-', help_text= '[kg]')
aha_classification = models.IntegerField(choices=AHA_CHOICES, default=0)
My Problem:
I don't know how to query the general data object with the number of examination = 1 for one special patient. I want to display the object on the detail page of the patient. I can query on the Examination class without problems. But then I just don't know how to query the generaldata object. The detail page loads only the Patient model. Due to this I have to query from the Patient model over the Examination model to the Generaldata model right? Or is it possible to load other models in the template? Thanks for your help!
Got it!
Added to my DetailView:
def DetailView(generic.DetailView):
model = Patient
template_name = 'app/detail.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(DetailView, self).get_context_data(**kwargs)
# Add in a QuerySet
context['FirstGeneral'] = GeneralData.objects.filter(examination__number_of_examination=1, examination__patient=get_object_or_404(Patient, pk=self.kwargs.get('pk')))
return context
"The detail page loads only the Patient model (...) is it possible to load other models in the template ?"
You don't "load" models "in the template", you pass them (or any other object you want) to the template's context - most often from your view code but actually from whereever you want to render a template. And yes of course you can pass just whatever you want, populating the template context is up to you.
The query which will work is:
Generaldata.objects.filter(examination__number_of_examination=1, examination__patient=Testpatient)
but this is in the wrong order.
Why is it "in the wrong order" ??? And if that query works what prevents you from using it ?
NB : if you're using generic DetailView, adding extra context is documented here

Django model references from a different model

I'm writing a method in a model and I need to access an attribute from another model.
from django.db import models
class Image(models.Model):
name = models.CharField(max_length=30)
image = models.ImageField(upload_to = slug_path)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
def slug_path(self):
# Need Article.slug from Article class here for constructing path
pass
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
slug = models.SlugField(max_length=50)
def __str__(self):
return self.headline
I want to write a method in the Image class that will have access to the slug of the Article it is included in via the one to many relation. Is this possible or is there a different way I should be going about this entirely?
Say if only one image can be related to one article, you need to add a field article in the image model which would be foreign key to Article model
article = models.ForeignKey(Article)
Now,
def slug_path(self):
slug = self.article.slug
return slug
Anyway, you can do it in a similar way for many to many fields etc.
Add a relationship to Image corresponding to the Article object, like you did with Article and Reporter.
article = models.ForeignKey(Article, on_delete=models.CASCADE)
Then to get/return the slug:
def slug_path(self):
return self.article.slug

Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model?

I've got a couple django models that look like this:
from django.contrib.sites.models import Site
class Photo(models.Model):
title = models.CharField(max_length=100)
site = models.ForeignKey(Site)
file = models.ImageField(upload_to=get_site_profile_path)
def __unicode__(self):
return self.title
class Gallery(models.Model):
name = models.CharField(max_length=40)
site = models.ForeignKey(Site)
photos = models.ManyToManyField(Photo, limit_choices_to = {'site':name} )
def __unicode__(self):
return self.name
I'm having all kinds of fun trying to get the limit_choices_to working on the Gallery model. I only want the Admin to show choices for photos that belong to the same site as this gallery. Is this possible?
Yes. You need to override the form that admin uses for the Gallery model, then limit the queryset of the photos field in that form:
class GalleryAdminForm(django.forms.ModelForm):
class Meta:
model = Gallery
def __init__(self, *args, **kwargs):
super(GalleryAdminForm, self).__init__(*args, **kwargs)
self.fields['segments'].queryset = Photo.objects.filter(site=self.instance.site)
class GalleryAdmin(django.contrib.admin.ModelAdmin):
form = GalleryAdminForm
django.contrib.admin.site.register(Gallery, GalleryAdmin)
I would delete site field on my Photo model and add a ForeignKey to Gallery. I would remove limit_choices_to from photos fields on Gallery model.
Because you are using ForeignKeys to Sites, that means sites don't share galleries and photos. Therefore having those I mentioned above is already useless.
class Photo(models.Model):
title = models.CharField(max_length=100)
gallery = models.ForeignKey(Gallery, related_name='photos')
file = models.ImageField(upload_to=get_site_profile_path)
def __unicode__(self):
return self.title
class Gallery(models.Model):
name = models.CharField(max_length=40)
site = models.ForeignKey(Site)
def __unicode__(self):
return self.name
Once you set the site on a gallery all its photos will inherit this property. And the site will be accessible as photo_instance.gallery.site:
#property
def site(self):
return self.gallery.site
This should work as if you had a site field. But I haven't tested it.
Things change or course, if you decide that a gallery or a photo can appear in multiple sites.
According to the docs, "limit_choices_to has no effect when used on a ManyToManyField with an intermediate table". By my reading, that means it has no effect at all, because ManyToManyFields use intermediate tables...
I haven't tried to make it work in the Admin site, but from your own views, you can create a form and override the queryset used to populate the list of choices:
form.fields["photos"].queryset = request.user.photo_set.all()

Categories