Left Join using 3 different tables - django - python

I have to show all articles from a law. Besides that, if the article is setted as marked in database (is_marked), I have to underline this article. For that, I need to do a left join, I mean, I have to show all articles, and if the article is marked I need to know that to underline it on the view. This marked must be specifc for each user.
my view:
def details(request, pk):
law = get_object_or_404(Law, pk=pk)
articles = Article.objects.filter(law=pk)
context = {
'articles': articles,
}
template_name = 'leis/details.html'
return render(request, template_name, context)
My detail.html:
<div class="article-post">
{% for article in articles %}
{{article}}
{% endfor %}
</div>
That's my model:
class Law(models.Model):
name = models.CharField('Name', max_length=100)
description = models.TextField('Description', blank = True, null=True)
class Article(models.Model):
article = models.TextField('Artigo/Inciso')
number = models.IntegerField('Number', blank=True, null=True)
law = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Law', related_name='articles')
This class is saved with highlights made by a specif user in a specific article in a specif law:
class Highlight(models.Model):
law = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Law', related_name='highlightArticles')
article = models.ForeignKey(Law, on_delete=models.CASCADE, verbose_name='Artigo', related_name='highlightLaw')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='highlightUsers', related_name='highlightUsers')
is_marked = models.BooleanField('Is it marked?', blank=True, default=False)
description = models.TextField('Description', blank = True, null=True)
How can I join the tables to show all the articles, marked with the specif highlights made by an specif user?

In newer versions of Django, you can perform subqueries inside other queries. Assuming you want to underline an article if the current user has highlighted the article, try the following:
from django.db.models import Exists, OuterRef
articles = (
Article
.objects
.filter(law=pk)
.annotate(
is_marked=Exists(
Highlight
.objects
.filter(
article_id=OuterRef('id'),
user=request.user
)
)
)
)
This way, we're performing a subquery for every article (don't worry about performance, the database engine will optimize this very well) to check if a Highlight object exists that matches the article and the current user.
Every article in this queryset will now contain an extra boolean member is_marked to tell you whether you should highlight it or not.
On a different note, there appear to be some problems with your model definitions. Highlight.article is currently a foreigh key to Law while I think this should be a foreign key to Article.

Related

How to display in the django template information of a manytomany model

I am practicing django but i am not able to display the following information from the database in the template.
I have these 3 models (PURCHASE ORDERS, MOVEMENTS, RECEIPTS):
class Ordenes_Compra(models.Model):
orden_compra=models.IntegerField(primary_key=True)
proveedor=models.ForeignKey(Proveedores, on_delete=models.CASCADE)
comedor=models.ForeignKey(Centros, on_delete=models.CASCADE)
fecha_documento=models.DateField()
fecha_entrega=models.DateField()
class Meta():
verbose_name='Orden de compra'
verbose_name_plural="Ordenes de compra"
class Movimientos(models.Model):
movimientos = \[
('ING', 'INGRESO'),
('SAL', 'SALIDA'),
\]
estados = \[
('PRO', 'PROCESADO'),
('ANA', 'ANALIZADO'),
('PEN', 'PENDIENTE'),
('CAN', 'CANCELADO'),
\]
cantidad_um=models.DecimalField(decimal_places=3, max_digits=20)
precio_por_um=models.DecimalField(decimal_places=3, max_digits=20)
mat_um=models.ForeignKey(Materiales, on_delete=models.CASCADE, editable=True)
orden_compra_mov=models.ForeignKey(Ordenes_Compra,on_delete=models.CASCADE)
tipo_movimiento=models.CharField(max_length=3, choices=movimientos, blank=True, null=True)
estado_movimiento=models.CharField(max_length=3, choices=estados, blank=True, null=True)
class Meta():
verbose_name='Movimiento'
verbose_name_plural="Movimientos"
class Imagenes_Comprobantes_Moviemientos (models.Model):
imagen_factura=models.ImageField(null=True, blank=True)
factura=models.CharField(max_length=20)
orden_compra_imagen=models.ManyToManyField(Ordenes_Compra)
class Meta():
verbose_name='Comprobante'
verbose_name_plural="Comprobantes"
Each PURCHASE ORDER can have several RELATED VOUCHERS and several VOUCHERS can have different PURCHASE ORDERS.
In the template, I am going to show a table with the MOVEMENTS, from which I want to give the information of the PURCHASE ORDERS that are related to that movement and the PROOFS related to that PURCHASE ORDERS:
I was thinking of something like this, but it doesn't work and I'm totally lost in what would be the best way to obtain the information of the receipts related to the purchase order related to the movement in the template:
THE SECOND FOR IS THE ONE IN WHICH I MAKE THE QUERY THAT WOULD NOT BE ABLE TO RESOLVE
{% for registro in movimientos %}
<tr>
<td>{{registro.cantidad_um|floatformat:2}}</td>
<td>{{registro.orden_compra_mov.orden_compra}}</td>
<td>{{registro.orden_compra_mov.comedor.nombre_centro}}</td>
<td>
{% for comprobante in registro.orden_compra_mov.orden_compra %}
{{comprobante}}
{% endfor %}
</td>
Thanks for your help!
(in case it is necessary I leave the view)
def movimientos(request):
movimientos = Movimientos.objects.annotate(total=F('precio_por_um') \* F('cantidad_um'))
return render(request, 'app/movimientos.html', {'movimientos':movimientos})
Problem when wanting to see in the template data from the postgresql database of a many-to-many relationship
I was able to solve it, the question was to access the inverse relationship through related_name.
I leave the documentation link: DJANGO DOCUMENTATION

"Object has no property _set" in Django

Can't figure out where my mistake is. Not able to map through to display the list of blog comments. I'm using Django and react. From the code below, I tried to assess each blog post with comments using foreign key. But I'm not able to get the comment property from the blog. If I do something like {blog.title} I get the title of the blog back on the browser. Since comments are associated with each post I try to get different properties of comment from the blog object (just as I specified in the code below) but the value I'm getting is undefined. And have the following blog post and blog comment models:
class BlogComment(models.Model):
post = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, related_name="post_comment", null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="user_comment", null=True)
name = models.CharField(max_length=200, null=True, blank=True)
comment = models.TextField(null=True, blank=True)
dateCreated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user.username)
class BlogPost(models.Model):
...
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
body = models.TextField()
dateCreated = models.DateTimeField(auto_now_add=True)
And the serializers for both models are:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = BlogComment
fields = '__all__'
class BlogPostSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField(read_only=True)
class Meta:
model = BlogPost
fields = "__all__"
def get_comments(self, obj):
comments = obj.comment_set.all()
serializer = CommentSerializer(comments, many=True)
return serializer.data
The endpint of comment is path('posts/<str:pk>/comment/', CreateCommentView, name="create-comment"),.
The endpoint is working. I'm able to add comment to posts both from the front end. The error comes when I try to map through the
comments for each post. Get the error: AttributeError: 'BlogPost' object has no attribute 'comment_set'.
Here is the code I'm using to map through to display all the blogs of a particular post in the blog details page in react. I'm assess each blog there:
<h2>{blog.title}</h2>
<img src={blog.image} />
<div variant='flush'>
{blog.comments.map((comment) => (
<div key={comment.id}>
<strong>{comment.name}</strong>
<p>{comment.dateCreated}</p>
<p>{comment.comment}</p>
</div>
))}
</div>
Here is the code I'm using to map through to display all the blogs of a particular post in the blog details page in react. If I do not map the error does not come up and I'm able to add comment. However, in order to display the comments under each blog post I map through. How do I fix this?
You need to use post_comment:
comments = obj.post_comment.all()
You declared it here:
post = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, related_name="post_comment", null=True)
related_name is used as a name for Django relation. See more here.
But changing post_comment with something else would be a better solution for me.

Django efficient Queryset with Foreign Key Models

I'm trying to find the most efficient way (as less db queries as possible) for the following model structure.
In my template I then want to pass all the data from all 3 models because I would have to show the post data as well as looping through the comments to create a comments list and display all the attachments for the different comments.
class Post(BaseModel):
user = models.ForeignKey('User', blank=True, null=True,
title = models.CharField(max_length=128)
content = models.TextField()
class Comment(BaseModel):
post = models.ForeignKey('Post', on_delete=models.CASCADE)
user = models.ForeignKey('User', on_delete=models.SET_NULL)
text = models.TextField()
class CommentAttachment(BaseModel):
comment = models.ForeignKey('Comment', on_delete=models.CASCADE)
name = models.CharField(max_length=128)
Should I fetch all data from CommentAttachment direction (meaning fetching all CommentAttachments where comment__post__id is the post id and then get all other data with select_related) or is there another way to start from the Post Model?
You can use prefetch_related or select_related in your query:
posts = Post.objects.filter(user=some_user).prefetch_related(
'comment_set', 'comment_set__commentattachment_set'
)
For example, after making a query as mentioned, the following command may retrieve all the comments for the first post in the queryset without making a SQL query:
posts.first().comment_set.all()

In Django, how do I get all the instances of a model that have other instances of a related model attached to it with a ForeignKey field?

I am trying to write a query that retrieves all the categories that have at least one post attached to it. In other words, I want to 'exclude' any category that doesn't have any post.
These are my models for Category and Post:
class Category(models.Model):
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique=True)
class Post(models.Model):
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique=True)
body = models.TextField()
category = models.ForeignKey(Category, blank=True, default="")
This is the code I am using in my query, currently it fetches all the categories, even if no Post is 'attached' to it:
categories = Category.objects.all()
What I want is something like this:
categories = Category.objects.filter(
# Only ones that have at least one Post that has it's 'category' field set to it.
)
I've searched the docs and everywhere else, but I can't figure out a solution.
Please let me know how this can be done.
You can use following query:
categories = Category.objects.filter(post__isnull=False).distinct()
This will get all the categories where post is not null. Since, one category could have multiple posts, you will get duplicate instances with same id. Take a distinct to remove duplicate categories.
Note, that distinct(*fields) is postgresql specific. If you are using a different database, just use distinct().
Get all unique category ids by querying Post for distinct category and then filter Category by ids.
id_list = Post.objects.values_list('category_id').distinct()
catgories = Category.objects.filter(id__in=id_list)

Django prevent duplicate queries after prefetch related

I'm working on a fairly simple library project in Django, here are my models, a book can have many copies and each copy can have many loans.
class Author(models.Model):
name = models.CharField(max_length=200, unique=True)
class Book(TimeStampedModel):
isbn = models.CharField(max_length=13, primary_key=True)
title = models.CharField(max_length=200, db_index=True, unique=True)
authors = models.ManyToManyField('Author', related_name='books')
#property
def is_available(self):
"""Returns True if the Book has any available copies"""
return self.copies.exclude(loans__returned=False).exists()
class BookCopy(models.Model):
book = models.ForeignKey('Book', related_name='copies')
class Loan(models.Model):
start_date = models.DateField()
end_date = models.DateField()
returned = models.BooleanField(default=False)
customer = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
null=True, related_name='loans')
book_copy = models.ForeignKey(
'BookCopy', on_delete=models.CASCADE, related_name='loans')
I have a fairly simple view containing a list of books
def book_list(request):
book_list = Book.objects.prefetch_related('authors', 'copies__loans')
return render(request, 'books/book_list.html', {'books': books})
To figure out if a book is available I've written the property is_available inside the Book model. However when I call this property inside my template with the following:
{% for book in books %}
{% if not book.is_available %}
-- Template stuff --
{% endif %}
{% endfor %}
According to Django Debug Toolbar I end up with a duplicate query for every Book in the queryset
Reading the Django documentation for prefetch related there's a section discribing the behaviour which I think may be the culprit
Remember that, as always with QuerySets, any subsequent chained methods which imply a different database query will ignore previously cached results, and retrieve data using a fresh database query.
How would I prevent these duplicate queries from occuring?

Categories