Django - filtering by user - python

I want to render data from the Route model that belongs to the Driver in their 'accounts' page - so displaying the leave_from, destination etc data they have saved in the database so far.
Models.py:
class Driver(models.Model):
user = models.OneToOneField(User, default=1)
first_name = models.CharField(max_length=120, blank=True, null=True)
last_name = models.CharField(max_length=120, blank=True, null=True)
tel = models.CharField(max_length=120, blank=True, null=True)
slug = models.SlugField(max_length=120, unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("account", kwargs={"slug": self.slug})
def save(self, *args, **kwargs):
self.slug = slugify(self.first_name)
super(Driver, self).save(*args, **kwargs)
class Route(models.Model):
leave_from = models.CharField(max_length=120, blank=True, null=True)
destination = models.CharField(max_length=120, blank=True, null=True)
date = models.DateField(auto_now_add=False, auto_now=False)
time = models.TimeField(auto_now_add=False, auto_now=False)
driver = models.ForeignKey(Driver, on_delete=models.CASCADE)
def __str__(self):
return self.leave_from
I've played with various querysets and the below is the closest to getting there (I think... I'm new to coding and Django).
Views.py:
def route(request, slug):
routedetails = Driver.objects.filter(route=request.user.driver.route_set.all())
context = {
"routedetails": routedetails,
}
return render(request, "route.html", context)
With that I am able to get user to display the same number of instances of data in Route for that Driver.
Template:
{% for route in routedetails %}
<p>{{ route.user }}</p>
{% endfor %}
I've tried all different variations but I feel this has got me the closest as it is at least returning the user the same number of times there is data in Route for this user. In this case there are 2 routes saved in Route and so the username is returned twice. I have tested on other users and it always matches.
I've looked everywhere and this is as far as I've been able to get so appreciate any help.

If you want Route details it is best to query the Route model directly:
routedetails = Route.objects.filter(driver__user=request.user)
You can then iterate through the Route objects in your template:
{% for route in routedetails %}
<p>{{ route.leave_from }}</p>
<p>{{ route.destination }}</p>
...
{% endfor %}

Pocket Kings' solution is great and should be accepted. This is an example if you want to show routes for multiple drivers (admin page?) in order to avoid N+1 queries. This pre-fetches all the routes associated to the drivers and adds an attribute routes to each driver with their specific routes, so that it would eliminate the unneeded SQL queries later.
from django.db.models import Prefetch
drivers = Driver.objects.all()
queryset = drivers.prefetch_related(Prefetch('route_set', queryset=Route.objects.filter(driver_id__in=drivers), to_attr='routes'))
Template
{% for driver in drivers %}
{% for route in driver.routes %}
<p>{{ route.leave_from }}</p>
<p>{{ route.destination }}</p>
...
{% endfor %}
{% endfor %}

To get logged in driver's routes, the simplest approach is.
views.py
routes = request.user.driver.route_set.all()
template
{% for route in routes %}
{{ route.leave_from }}
{{ route.destination }}
{% endfor %}

Related

Getting the number of comments from a specific post in django

I have a little problem with a query. I work on a blog website with django. For posts I have the first page where i display all the posts as a list, with their details (title, date posted etc.) and I want to display the number of comments for each post along with title, date posted and tags. I'm not sure how to make that, I need to implement something on the model classes or in view function that renders the page ?
Here are the model classes.
class Post(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
content = models.TextField()
tags = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment_text = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return f'{self.user.username} Comment ' + str(self.id)
and the view function
def blog(request):
context = {
'posts': Post.objects.all(),
'title': 'Blog',
'banner_page_title': 'Blog',
'page_location': 'Home / Blog'
}
return render(request, 'blog/blog.html', context)
Method 1:
You can use this in your template.
{% for post in posts %}
{{ post.comment_set.count }}
{% endfor %}
Method 2:
You can implement a model method like this:
class Post(models.Model):
....
def __str__(self):
return self.title
#property
def comment_count(self):
return self.comment_set.count()
And you can call the model method in your template like this:
{% for post in posts %}
{{ post.comment_count }}
{% endfor %}
Use a reverse lookup in the template and count to get the number of related objects:
{{ post.comment_set.count }}
Something like this?
template.html
...
{% for post in posts %}
<div> Title: {{post.title}} </div>
<div> Date Posted: {{post.date_posted}} </div>
<div> Number of Comments: {{post.comment_set.count}} </div>
{% endfor %}

How to return multiple items in a get_queryset function in Django?

So I have a web app where the user can enter their information, and eventually I want to display all of it, but at the moment this code right here
class UserPostListView(ListView):
model = Post
template_name = 'mainapp/user_posts.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
first_name = get_object_or_404(User,first_name=self.kwargs.get('first_name'))
return Post.objects.filter(author=user).order_by('-published_date')
It gives me an error, and it says User not found.
I have tried add this to the end of the return statement
.order_by('-published_date'),first_name
However this did not work.
This is the relevant urls.py file responsible for the user posts
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
This is the UserProfileInfo model
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
description = models.CharField(max_length=150)
image = ProcessedImageField(upload_to='profile_pics',
processors=[ResizeToFill(150, 150)],
default='default.jpg',
format='JPEG',
options={'quality': 60})
joined_date = models.DateTimeField(blank=True,null=True,default=timezone.now)
verified = models.BooleanField( default=False)
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
And here is the relevant bit of the user-posts.html
<div class="sidebar">
<p class="active" href="#">{{ view.kwargs.username }}</p>
<button class="commentbtn"><a class="aclass" href="#">Connect with {{ view.kwargs.username }}</a></button>
<p>{{ view.kwargs.first_name }}</p>
<p>Lorem</p>
</div>
I want to be able to display the first name of the person in the ```{{ view.kwargs.first_name }}, however everything I have tried has failed to work
I expected no errors to occur and for this to work, however this is not the case. Is it possible to have 2 get_queryset methods, or is there something I can do in the current one to achieve my goal of displaying the information of the user
Thanks for any help you can give :)
How about this?
def get_queryset(self):
qs = super().get_queryset() #this is the same as Post.objects.all()
user = self.request.user
return qs.objects.filter(author=user).order_by('-published_date')
Now you can access this query using object_list on your template, but since you changed it's name with context_object_name='posts', use posts instead:
{% for post in posts %}
<h1>{{ post.first_name }}</h1>
...
{% endfor %}
But it looks like the Post model isn't going to have a first_name column?
You might be better off using the request.user object here as well:
<h1>{{ request.user.first_name }}</h1>
<h2>{{ request.user.username }}</h2>

how to connect two models in html template?

I would like to create a simple forum with Python Django. The main thing I can not figure out is getting informations from two models.
I want to display: Post title, content, author, published date and signature. This is my post_detail.html
{% extends 'Homepage/base.html' %}
{% block content %}
<h1>{{ post.title }}</h1>
<h6>Kategoria: {{ post.category }} | Autor: {{ post.author }} | {{ post.published_date }}</h6>
<p>{{ post.description|linebreaksbr }}</p>
<p><hr>{{ HERE I DON'T KNOW HOW TO SHOW SIGNATURE }}</p>
{% endblock %}
</body>
</html>
Homepage app models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.title
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
published_date = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(blank=True, null=True)
views = models.IntegerField(default=0)
def __str__(self):
return self.title
accounts app models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.CASCADE)
website = models.URLField(default='', blank=True)
city = models.CharField(max_length=100, default='', blank=True)
signature = models.TextField(default='', blank=True)
def create_profile(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
user_profile = UserProfile(user=user)
user_profile.save()
post_save.connect(create_profile, sender=User)
def __str__(self):
return self.user.username
My full code is on https://github.com/Incybro/Forum
You can just follow the relationships:
<p>{{ post.author.user.signature }}</p>
(Note, you've set the related_name from User to UserProfile to user, which makes no sense. You should leave it as the default, which would be userprofile.)
I guess your {{post.author}} won't be returning anything, change in to {{post.author.get_full_name}}
For signature, in your model you don't need to add any related name,
user = models.OneToOneField(User, on_delete=models.CASCADE)
in template,
{{post.author.userprofile.signature}}
Why not just return the relevant UserProfile model when you send the request?
def my_view(request):
context = {}
my_post = Post.objects.all.get(0) # get the post here
my_user = UserProfile.objects.get(user=my_post.author)
context['post'] = my_post
context['user'] = my_user
return TemplateResponse(request, 'my_template.html', context)
Then, in your html, you can use those template tags.
{% block content %}
<h1>{{ post.title }}</h1>
<h6>Kategoria: {{ post.category }} | Autor: {{ post.author }} | {{ post.published_date }}</h6>
{% autoescape on %}
<p>{{ post.description|linebreaksbr }}</p>
<p><hr>{{ user.signature }}</p>
{% endautoescape %}
{% endblock %}
You want the autoescape so people can't change the look or function of your site by injecting malicious HTML/JS into your site.

django Get foreign-key model in view and show in html

So I have two models:
class UserMeasurements(models.Model):
user = models.CharField(max_length=20)
time = models.DateTimeField(blank=True, null=True)
set = models.ForeignKey(PhotoSets, models.DO_NOTHING)
class Meta:
managed = False
db_table = 'user_measurements'
class PhotoSets(models.Model):
image_dir = models.CharField(max_length=200)
machine = models.ForeignKey(Machine, models.DO_NOTHING)
set_id = models.AutoField(primary_key=True)
photo_taken_time = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'photo_sets'
In the view.py I have :
def Main(request):
measurement_list = UserMeasurements.objects.order_by('-time')
photo_set_list = PhotoSets.objects.all()
a = measurement_list.set.get_field('PhotoSets').rel.to
//this is where I try to get the related models
print str(a)
return render(request, 'main.html', {'measurement_list': measurement_list})
I am trying to show the image_dir in the webpage with every User Measurement.
I checked other stackoverflow questions. But still I can't figure it out.
Thanks in advance.
I don't know what you're trying to do with that a variable, but you don't need to do any of this. You should just follow the foreign key when you output each measurement, eg in your template:
{% for measurement in measurement_list %}
User: {{ measurement.user }}
Time: {{ measurement.time }}
Image dir: {{ measurement.set.image_dir }}
{% endfor %}
Note that this is explained quite fully in the docs, especially in the tutorial where they use the example of poll questions and choices.
Would you give it a try in your main.html
{% for item in measurement_list %}
{{ item.image_dir }}
{% endfor %}

Extra "row-level" data in ListView django

I'm writing my first django app and can't seem to pass "row-level" data to the template through ListView. Specifically I'm trying to show all Polls and their corresponding Vote information using a PollListView.
Currently I am only able to pass all votes to the template, but would like to pass only the votes that belong to the specific poll.
models.py
class Poll(models.Model):
user = models.ForeignKey(User, unique=False, blank=False, db_index=True)
title = models.CharField(max_length=80)
class Vote(models.Model):
poll = models.ForeignKey(Poll, unique=False, blank=False, db_index=True)
user = models.ForeignKey(User, unique=False, blank=True, null=True, db_index=True)
vote = models.CharField(max_length=30, blank=False, default='unset', choices=choices)
views.py
class PollListView(ListView):
model = Poll
template_name = 'homepage.html'
context_object_name="poll_list"
def get_context_data(self, **kwargs):
context = super(PollListView, self).get_context_data(**kwargs)
context['vote_list'] = Vote.objects.all()
return context
urls.py
urlpatterns = patterns('',
...
url(r'^$', PollListView.as_view(), name="poll-list"),
}
homepage.html
{% for poll in poll_list %}
{{ poll.title }}
{% for vote in vote_list %}
{{ vote.id }} {{ vote.vote }}
{% endfor %}
{% endfor %}
Seems like an easy task, but I can't seem to figure out how to do this using class-based views. Should I be using mixins or extra_context? Overwrite queryset? Or should I just used function-based views to solve this.
Any help would be greatly appreciated.
I'm not sure if it's gonna work, but you can try the following:
models.py (Vote class)
poll = models.ForeignKey(Poll, related_name="votes", unique=False, blank=False, db_index=True)
views.py
class PollListView(ListView):
queryset = Poll.objects.all().prefetch_related('votes')
with that you can access related votes:
template
{% for poll in poll_list %}
{{ poll.title }}
{% for vote in poll.votes.all %}
{{ vote.id }} {{ vote.vote }}
{% endfor %}
{% endfor %}

Categories