Only Add Certain Fields in Variable - Python Function - Django - Views - python

I have Django model with CharFields 'flname1', 'date1', and 'time1'. My goal in my HTML is to have a {{ forloop }} that runs through only the 'date1' and 'time1' fields and displayed all of them. My Problem is that in my views file I can't find a way to create a python variable that only contains two of the three fields from one model. Ie tried a lot but what I'm trying to avoid is...
posts = DocPost.objects.all()
This puts all three fields into a variable and displays them in my for loop which I don't want. I've also tried a lot of work with filters and go things that other people on the internet had success with that didn't work for me like...
posts = DocPost.objects.filter('flname1').only('date1', 'time1')
This didn't work and didn't section off date1 and time1 and pack them away in a variable that I could loop through. Ive tried a lot more than this at this point to no prevail. Thank for any help.

There are two things you can do to only get certain fields in a query and iterate over them. The template for both is pretty much the same
First, you can use only() to generate a queryset where each object only has certain fields populated and all the rest are deferred
# View
context['posts'] = DocPost.objects.only('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}
Second, you can use values() to generate a queryset of dictionaries that only contain the fields specified
# View
context['posts'] = DocPost.objects.values('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}

Related

What will be the most efficient way to filter the objects in django

I am working on a basic ecommerce website.I have created a superuser page in which I would like to see all the orders as well as the order details like customer who ordered, their address etc. These are my models.py:
class ShippingAddress(models.Model):
customer=models.ForeignKey(Customer,on_delete=models.SET_NULL,null=True)
order=models.ForeignKey(Order,on_delete=models.SET_NULL,null=True)
address=models.CharField(max_length=200,null=False)
email=models.EmailField(null=False)
class Customer(models.Model):
user=models.OneToOneField(MyUser,null=True,blank=True,on_delete=models.CASCADE)
email=models.CharField(max_length=100)
class Order(models.Model):
customer=models.ForeignKey(Customer,on_delete=models.SET_NULL,null=True,blank=True)
complete=models.BooleanField(default=False,null=True,blank=False)
class OrderItem(models.Model):
product=models.ForeignKey(Product,on_delete=models.SET_NULL,null=True)
order=models.ForeignKey(Order,on_delete=models.SET_NULL,null=True,)
And this is my views.py:
def superuser(request):
user=User.objects.all()
customer=Customer.objects.all()
order=Order.objects.filter(complete=True)
items=OrderItem.objects.all()
shipping=ShippingAddress.objects.all()
return render(request,"superuser.html",{'order':order,'items':items,'customer':customer,'shipping':shipping})
Currently in my template I am unable to iterate over the above context such that I can get all the orders and with the every order I can print their orderitems as well as shipping details as well as customer details. I tried in one way which was really in efficient that was to iterate over all orders then iterate over all orderitems and check if orderitem.order.id was equal to order.id . Please tell me what is the best method to pass the objects in context which are the most efficient for my need . And how to iterate over them in my template.
Thanks
How about this?
# view
from django.shortcuts import render
from .models import Order
def superuser(request):
orders = Order.objects.select_related('customer__user')
orders = orders.prefetch_related('shippingaddress_set')
orders = orders.prefetch_related('orderitem_set')
return render(request,"superuser.html",{'orders':orders})
You can of course chain the .select_related and .prefetch_related calls on the same lines, I've split them up here for increased readability. You can read about select_related and prefetch_related in the docs. You can now use the 'orders' QuerySet in a template like this:
<!--template-->
{% if orders %}
<ul>
{% for order in orders %}
<li>order id: {{order.id}}</li>
<li>customer name: {{order.customer.id}}</li>
<li>customer email: {{order.customer.email}}</li>
{% if order.shippingaddress_set.all %}
<li>Shipping addresses:
<ul>
{% for shipadd in order.shippingaddress_set.all %}
<li>shipping address: {{shipadd.address}}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% if order.orderitem_set.all %}
<li>Order items:
<ul>
{% for item in order.orderitem_set.all %}
<li>orderitem id: {{item.id}}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
Using Django Debug Toolbar and going to the page above, I'm informed that 3 SQL queries are made:
One fetches data from the 'order' table, doing a LEFT OUTER JOIN with the 'customers' table ON customer id.
One fetches data from the 'shippingaddress' table, with a WHERE clause specifying rows that have a 'order_id' IN (<list of all order id's>).
A similar query is made for the 'orderitem' table.
This way, Django preemptively fetches all the required data from the database, rather than e. g. doing another query for every iteration.
You might find it strange that I included a loop for the shipping addresses in the HTML template. This is however necessary, because the way you've set up your models, there is a many-to-one relationship from shipping addresses to orders. This doesn't make a lot of sense IMO, so you will probably want to redefine the ShippingAddress model's relationships to the other models.
If you want to use additional information related to each order, you can of course add more prefetch_related/select_related calls before putting the QuerySet in your context.

django - filtering objects across multiple templates?

I'm trying to filter objects across two templates. One (the parent) should display the five most recently updated, and the other should display all of them.
I have the latter working perfectly with the following code:
views.py:
...
class ChannelProjectList(generic.ListView):
context_object_name = 'projects_by_channel'
template_name = 'channels/projects_by_channel.html'
def get_queryset(self):
self.channel = get_object_or_404(Channel, slug=self.kwargs['slug'])
return Project.objects.filter(channel=self.channel)
...
HTML:
{% for project in projects_by_channel %}
{{project.name}}
{% endfor %}
But when I go to "include" it on the parent page it breaks. After some research I understand why that is happening and why that isnt the proper way to do it. I dug around and found this, which seems to be exactly what I'm trying to do but when I implemented it, not only did it not work it but also broke the page that is working.
This feels like a pretty simple thing, but since this is my first project I'm running into new things every day and this is one of them.
Final Solution:
With the help of this I realised I needed to copy in the same get_queryset into the second template view, which I was then able to call into the template using "view.channel_projects"
You have two possibilities. First you could define two context variables (like its done in your linked solution) or you could slice the qs in the template.
1. Option slice:
This one would display all:
{% for project in projects_by_channel %}
{{project.name}}
{% endfor %}
This on only displays 5 entries:
{% for project in projects_by_channel|slice:":5" %}
{{project.name}}
{% endfor %}
2. Option define two query sets:
(views.py)
def get_queryset(self):
self.channel = get_object_or_404(Channel, slug=self.kwargs['slug'])
self.channel2 = get_object_or_404(Channel, id=12)#whatever
context["list"] = Project.objects.filter(channel=self.channel)
context["list2"] = Project.objects.filter(channel=self.channel2)[0:5] #this slices the query set for the first entries. If you want to order them first(by date or whatever) use "order_by()"
return context
(html)
{% for project in list %}
{{project.name}}
{% endfor %}
{% for project in list2 %}
{{project.name}}
{% endfor %}
If you want to display a single qs but one time the whole thing and in another template just the first 5 you are better suited with using the slice argument in the template. It keeps the view clean and simple and you don't have to query two times.
I hope that helps if not leave a comment.

How to go through a ManyToMany field in chunks in Django

I am trying to display sets of three objects in a HTML template in a tiled interface. So, for example, an Album model has a ManyToMany field with specific photos. I want to iterate through the photos and show them in sets of three in the view. Currently, I can get all the photos using {{ for photo in album.images.all }} in the template, but don't know how to get the results in sets of three.
How would I go about chunking the results into sets of three so that I can then iterate through the sets of three for the template? Or is there a way to get the total length and then index of specific elements using the Template tags?
Thanks
{% for photo in album.images.all %}
{{ photo }}
{% if forloop.counter|divisibleby:4 %}
<hr> {# or some other markup #}
{% endif
{% endfor %}
You might also do the grouping in your view. This might result in cleaner markup. See: Django template {%for%} tag add li every 4th element

Django templates - Conditionally displaying a button for logged in users

I have a page in my Django app that needs to do one of the following depending on the status of the logged in user in relation to a group (not a Django user group; something custom to my app) represented on the page:
If the user can join the group, display a link to join the group.
If the user is in the group, display a link to leave the group.
If the user can't join the group, don't display either link.
One way of doing this would be to create three templates (one with the join link, one with the leave link, and one with no link) and choose the appropriate one in the view. I feel like it may be overkill to have three different templates that will only differ in one line of code, so I have not gone that route as of yet.
Displaying the correct content for conditions 1 and 2 exclusively using a template is not possible, and if it were I do not think it would be advisable. Users to groups is a many-to-many relationship, and determining group membership requires passing a user to the group or passing a group to the user.
Since Django templates don't allow passing function arguments, I am trying to solve this by passing a context variable to the template using get_context_data.
def get_context_data(self, **kwargs):
context = super(NetworkDetails, self).get_context_data(**kwargs)
user = ???
context['in_group'] = user.in_group(context['group_detail'])
return context
If I do that, how can I get the currently logged in user within that method? If that isn't possible, where else can I get that information outside of the template? Is there an accepted method of doing something like this?
Thanks!
One way of doing this would be to create three templates (one with the
join link, one with the leave link, and one with no link) and choose
the appropriate one in the view
That's funny because if you can choose what template to include, you can just choose what html to display. Instead of:
{% if join_link %}
{% include 'join_link.html' %}
{% endif %}
{% if leave_link %}
{% include 'leave_link.html' %}
{% endif %}
{% if not join_link and not leave_link %}
you can't join
{% endif %}
You could just have:
{% if join_link %}
join
{% endif %}
{% if leave_link %}
leave
{% endif %}
{% if not join_link and not leave_link %}
you can't join
{% endif %}
So, I don't understand why you want to use template inclusion.
If I do that, how can I get the currently logged in user within that method?
self.request.user
self.request.user.is_authenticated() # return True if the user is logged in
You can determine the condition in your view and pass appropriate flag(s) to the template using context.
If there are multiple views/templates that need this info, you could implement custom context processor which can add this info in context and its available in each template.
Or If you have any OneToOne or any such relationship with User in your app, you can implement method in that model.
You can check if a user is logged in by checking permissions
https://docs.djangoproject.com/en/dev/topics/auth/ also you can do the similar for your other needs.

Django template check for empty when I have an if inside a for

I have the following code in my template:
{% for req in user.requests_made_set.all %}
{% if not req.is_published %}
{{ req }}
{% endif %}
{% empty %}
No requests
{% endfor %}
If there are some requests but none has the is_published = True then how could I output a message (like "No requests") ?? I'd only like to use Django templates and not do it in my view!
Thanks
Even if this might be possible to achieve in the template, I (and probably many other people) would advise against it. To achieve this, you basically need to find out whether there are any objects in the database matching some criteria. That is certainly not something that belongs into a template.
Templates are intended to be used to define how stuff is displayed. The task you're solving is determining what stuff to display. This definitely belongs in a view and not a template.
If you want to avoid placing it in a view just because you want the information to appear on each page, regardless of the view, consider using a context processor which would add the required information to your template context automatically, or writing a template tag that would solve this for you.

Categories

Resources