Django querysets. How to prefetch only unique? - django

Models:
class House(Model)
class Flat(Model):
house = ForeignKey(House, related_name="houses")
owner = ForeignKey(User)
class User(Model)
queryset:
queryset = User.objects.prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("houses"))
And then flats:
{% for flat in user.flats.all %}
<p>№ {{ flat.number }}, {{ flat.house.name }}</p>
{% endfor %}
It's fine. But for houses I need only unique ones
{% for flat in user.flats.all %}
<p>House {{ flat.house.name }}</p>
{% endfor %}
But this template gives me ALL houses, with duplicates.
How can I avoid duplicates, any ideas? I tried .distinct() but it's dosen't work, looks like I using distinct() wrong or etc.

It seems you may end up with duplicate houses if a user is the owner of multiple flats, which are all in the same house. To get all houses that the user is related to via Flats, you can use a different query, starting with House so that you can use distinct:
distinct_houses = House.objects.filter(flats__owner=user).distinct()
The above returns one House object for each flat where the user is an owner, and makes sure you don't have duplicates. (Note that the above assumes the related_name is changed to flats for house = ForeignKey(House, related_name="flats"), since "houses" as the related_name seems like a typo to relate back to the Flat model.)
Alternatively, you could do something in-memory in Python, if you still also need to know all Flats via your original queryset = User.objects.prefetch_related(...) and don't want an additional query. Like:
distinct_houses = {flat.house for flat in user.flats.all()}
Side note: it looks like you have a typo in needing to use Flat.objects.select_related('house') rather than Flat.objects.select_related('houses') based on the Foreign Key field name being house.

Only found solution with template filters.
Queryset:
queryset = User.objects.filter(status="ACTIVE").prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("house"))
)
With this I can get all flats for each user:
{% for user in object_list %}
{% for flat in user.flats.all %}
{{ flat.number }}, {{ flat.house.name }}
{% endfor %}
{% endfor %}
And I am using template filter to get unique houses.
{% for user in object_list %}
{% for flat in user.flats.all|get_unique_houses %}
{{ flat.house.name }}
{% endfor %}
{% endfor %}
# extra_tags.py
#register.filter(name="get_unique_houses")
def get_unique_houses(value):
return value.distinct('house')

Related

How to filter a ManyToMany field from django template?

I have a model named Universities in which there is a ManyToMany field named bookmarks associated with User model. In template file, I have looped through all the universities {% for university in universities %} and I am trying to show a bookmark icon based on the logged in user has bookmarked that specific university or not. However, it seems like I can not filter the query in template directly. How do I do that?
Don't name your models with plural names. "universities" should be "university" because every instance of the model represents a single university.
You can access one to many and many to many relationships in the templates. How you do that depends if you have assigned a related_name to the relationship.
Let me show you an example:
class University(models.Model):
name = models.CharField(max_length=50)
bookmarks = models.ManyToManyField(Bookmark, on_delete="models.CASCADE", related_name="universities")
Then you pass a list of all university models to the template:
class MyView(View):
def get(self, request):
context = { 'universities' : University.objects.all() }
return render(request, "mytemplate.html", context)
And finally you access all that you need from the template. This part is slightly tricky but not too much.
{% for university in universities %}
{% for bookmark in university.bookmarks.all %}
{{ bookmark }}
{% endfor %}
{% endfor %}
If you were to instead pass to the template a list of Bookmark instances, then you would have used the related_name stated in our example.
So like this:
{% for bookmark in bookmarks %}
{% for university in bookmark.universities.all %}
{{ university }}
{% endfor %}
{% endfor %}
If you didn't specify a related_name, then you can access the same data using the _set convention:
{% for bookmark in bookmarks %}
{% for university in bookmark.university_set.all %}
{{ university }}
{% endfor %}
{% endfor %}

Receiving a list of related models from the list of objects (bound by a foreign key). Django

I have two very simple classes for products and photos. I would like to present products on the main page of my store along with photos related to a foreign key. But I do not know how to do this, I found many answers to get this in the view using a join like 'prefetch_related' but my ID changes for each case. So how do you present your photos for each product? are there any Django tags about which I do not know?
my models.py
class Product(models.Model)
name = models.CharField(max_length=10)
class Image(models.Model)
image = models.ImageField()
name_related = models.ForeignKay(Product, on_delate=models.CASCADE)
views.py
def home(request):
product_list = Product.objects.all()[:12]
#img = ??
context = {'product_list': product_list,
}
return render(request, 'home.html', context)
home.html
{% for product in product_list %}
{{ product.name }}
<!-- {{ product.imge }} ' element presenting the first photo for each 'product' model'???-->
{% endfor %}
Any help will be appreciated.
Since a foreign relation has been established you can iterate through the related models from the parent model.
The related models are already accessible from the parent model without doing anything explicit.
In your template you can do this:
{% for product in product_list %}
{{ product.name }}
{% for product_image in product.image.all %}
<!-- Iterate through this products related images -->
{{ product_image.image.url }}
{% endfor %}
{% endfor %}
For performance reasons, you will want to prefetch the relations, or else for every product you will do an additional query, so, in your view you will want to add this:
product_list = Product.objects.all().prefetch_related('image')[:12]
Just do
{% for product in product_list %}
{{ product.name }}
{% for image in product.image.all %}
<!-- {{ image.image.url }} -->?
{% endfor %}
{% endfor %}

Django: one-to-many relationship and reverse lookup

i have a Problem which i am unsure what the most django/pythonic way to solve it would be.
I have the following models:
class Order(models.Model):
ord_someinformation = models.CharField(max_length=10)
class Articles(models.Model):
myFK = models.ForeignKey(Order)
detail_article= models.CharField(max_length=10)
so every Order can have multiple Order_Details think about it like a shopping basket where i have an Order and within it i have multiple articles.
I want to iterate over the orders and the articles within the template. I thought about something like.
myOrder = Order.objects.("" i have no idea what to put here "")
within the template i thought about something like this:
{% for order in myOrder %}
{{ order.ord_someinformation }}
{% for articles in order.articles %}
{{ detail_article }}
{% endif %}
{% endif %}
is this possible?
If so how ?
I don't know why you think you need to put anything there. You just want to send all the orders to the template, then iterate through them their articles there.
myOrder = Order.objects.all()
...
{% for order in myOrder %}
{{ order.ord_someinformation }}
{% for article in order.articles_set.all %}
{{ article.detail_article }}
{% endif %}
{% endif %}

django ModelChoiceField: how to iter through the instances in the template?

I am trying to get access to the instances of a ModelChoiceField to render them the way I want in a template by displaying several fields of the instances.
class MyForm(forms.Form):
mychoices = forms.ModelChoiceField(
queryset=ObjectA.objects.all(), widget=forms.RadioSelect(), empty_label=None
)
This does not work:
{% for choice in form.mychoices %}
{{ choice.pk}}
{% endfor %}
I also tried to use the queryset but it does not render anything
{% for choice in form.mychoices.queryset %}
{{ choice.pk}}
{% endfor %}
Any idea?
Thanks
{% for choice in form.mychoices.field.queryset %}
{{ choice.pk }}
{% endfor %}
Notice the extra .field. It's a bit strange the first time you come across it, but it gives you what you want. You can also access the choices attribute of that object, instead of accessing queryset directly, but you'll need to access the first element of the choice to get the PK of the instance, like this:
{% for choice in form.mychoices.field.choices %}
{{ choice.0 }}
{% endfor %}

Iterate through model fields in jinja template

I have several models in my application, and as I will have some views doing the same thing (form + tables showing existing records in my model), but implemented dfferently because of the model, which will be different in each case, I was wondering if it was possible to make it generic.
I googled a bit and was not able to find anything relevant to my case.
What I would like to achieve:
In my view, I want to go through each object from the model that I passed to the template, for example:
return render_template('addstatus.html', form=form, statuses=Status.query.all(),
target_model="Status", fields=Status.__mapper__.c.keys())
But I want to have only one view, whatever the model will be, so I am not able to know in advance the fields of my model, the number of columns and so on.
I want to have something like that in my view:
{% for obj in objects %}
{% for field in obj.fields %} (not existing)
<h1> {{ field }} :: {{ obj.fields.field.value }}
{% endfor %}
{% endfor %}
Is it something possible? How can I achieve that?
You can add this method to your db.Model class (either by subclassing or by monkeypatching):
from sqlalchemy.orm import class_mapper, ColumnProperty
def columns(self):
"""Return the actual columns of a SQLAlchemy-mapped object"""
return [prop.key for prop in class_mapper(self.__class__).iterate_properties
if isinstance(prop, ColumnProperty)]
Then you can use {% for col in obj.columns() %} to iterate over the column names.
I had a similar issue, and wanted something purely Jinja2 based. This will return all of the keys/fields in your "obj" table, and worked well for me:
obj.__table__.columns._data.keys()
Here it is implemented in your sample code
{% for obj in objects %}
{% for field in obj.__table__.columns._data.keys() %}
<h1> {{ field }} :: {{ obj[field] }} </h1>
{% endfor %}
{% endfor %}