So I have two models: Thread, ForumPost
With a many-to-one relationship (each Thread can have many ForumPosts).
I would like to display the author of the last post made into that thread.
I'm currently doing:
threads = Thread.objects.annotate(replies=Count('forumpost'), lastpost=Max('forumpost'))
And I can access it in my view by doing:
<td>{{thread.lastpost}}</td>
This works fine - but it doesn't do what I want - this only displays the last post ID (which is logical, because I'm displaying the post), not the author. So when I try to do:
<td>{{thread.lastpost.author}}</td>
It won't display the author (it's blank), although I have
author = models.CharField(max_length=200)
In my ForumPost model. So how do I go on about displaying the author, not the ID?
Models:
class Thread(models.Model):
title = models.CharField(max_length=50)
class ForumPost(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
author = models.CharField(max_length=200)
text = models.CharField(max_length=50)
You need a subquery expression.
newest = ForumPost.objects.filter(post=OuterRef('pk')).order_by('-pk')
threads = Thread.objects.annotate(last_post_author=Subquery(newest.values('author')[:1]))
Now each thread has a last_post_author attribute with the value of the author of the last post.
Note, you should really have a more specific field to order on - pk will usually be chronological, but it's not guaranteed. Add a created_at field with auto_now_add and order by that in the subquery.
Related
The following is in my models.py:
class SensorType(models.Model):
hardware_type = models.CharField(max_length=100)
is_static = models.BooleanField(default=False)
# Some other fields
class Sensor(models.Model):
device_id = models.CharField(max_length=100, primary_key=True)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT)
# Some other fields
class Asset(models.Model):
name = models.CharField(max_length=100)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT) # I need to use this field to filter below
sensor = models.ForeignKey(Sensor, on_delete=models.PROTECT, limit_choices_to={'sensor_type': WHAT DO I PUT HERE?},)
# Some other fields
I need to limit the choices in the sensor field of asset so that only sensors with the sensor_type set in the field immediately above, show up.
The reasoning behind this is that there will eventually be many sensors and it would be very useful to filter this. Initially I only need this to work from the admin page but this will eventually extend when I make my Create and Update Views.
Is this even possible? I'm essentially trying to access attributes before the object has actually been created.
After reading several other questions such as this one I have also looked into ModelChoiceField but the same issue exists of trying to access the form data before it has been submitted.
I'm very open to changing the model structure if that is what is required.
Similar to this: User defined fields model in django
I am creating a COVID Prescreening system for a school project. Event creators will be able to create forms, which consist of basic questions such as temperature, contact with covid in the last 14 days, etc. as well as provide custom questions for the attendee to answer which I cannot predict.
For example, the event creator could ask 2 questions:
How are you feeling today?
Have you been to a party in the last week?
And every attendee for that event would have to fill out these 2 questions in addition to the standard questions.
The model for this is:
class Event(models.Model):
''' model for an event '''
creator = models.ForeignKey("Account", on_delete=models.CASCADE)
title = models.CharField(max_length=200, help_text="Enter a title for this event")
start_time = models.DateTimeField()
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
custom_questions = models.ManyToManyField(CustomQuestion)
def __str__(self):
return f'{self.title} {self.uuid}'
Each custom question is essentially a key/value model:
class CustomQuestion(models.Model):
question = models.CharField(max_length=200)
response = models.CharField(max_length=200, blank=True)
The user will fill out the COVID Form, which will create an object as such:
class CovidScreenData(models.Model):
custom_responses = models.ManyToManyField(CustomQuestion)
temperature = models.DecimalField(max_digits=5, decimal_places=2, default=98.6)
contact_with_covid = models.BooleanField(default=False)
This data is embedded in the larger response, which ties everything together
class Response(models.Model):
''' model for a completed covid screen '''
account = models.ForeignKey('Account', on_delete=models.SET_NULL, null=True)
time = models.DateTimeField()
event = models.ForeignKey('Event', on_delete=models.CASCADE)
details = models.ForeignKey('CovidScreenData', on_delete=models.CASCADE)
def __str__(self):
return f'{self.account.user.username}\'s Response ({self.event.title})'
When the attendee is filling out the form, I want them to be given the custom_questions for the event they are filling out.
My idea is that when they are presented with the form, each question in custom_questions will be looped through and displayed. When the user submits, their response, as well as the original question, are saved in the custom_responses variable.
What is the correct organization to do this? I am asking this rather than how do I display the questions to the user and save their responses in the model.
If you want to save the response correspond to the question I think you can not use ManytoMany field on this part
class Event(models.Model):
...
custom_questions = models.ManyToManyField(CustomQuestion)
...
you should use ManytoOne for Event relationship with CustomQuestion (adding foreign key to Event on CustomQuestion). It's because you store the answer on the same row with the question so other event should not use the same row of CustomQuestion(containing question and answer).
also you can't store a ManytoMany in the CovidScreenData about the custom_response that actually contains CustomQuestion because the reason on my first explanation.
If you want to get and store the answer just go through the Response item, then get the Event item, and then get pairs of question-answer using foreign key on CustomQuestion join with the Event item.
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()
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)
I've got this Post model at the moment:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
If I've got different parts of a website (or a forum rather) that contain different posts, e.g. Discussion about basketball, and Discussion about football, if I wanted to return just posts concerning basketball or just posts concerning football, is the easiest way to just make a specific basketball_post model/football_post model or is there a more efficient way? Should I perhaps be storing the values differently?
Thanks
Django has a really good tutorial. It is about making a Poll app. In the first chapter the thing you want is discussed. It is about a Question that can have multiple Choices.:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
The foreignKey creates a relation between two models. The same can be done for a blog:
class Category(models.Model):
title = models.CharField(max_length=200)
class Post(models.Model):
category = models.ForeignKey(Category) # This is the important part.
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
The ForeignKey relation lets you do really nice things:
basketball_posts = Post.objects.filter(category_title='Basketball')
But before we all tell you how it is done, I really recommend to do the tutorial. It introduces you to all important Django concepts: https://docs.djangoproject.com/en/1.7/intro/tutorial01/
Update
If you have a fixed set of categories that are not likely to change, than you can hardcode them and use field choices:
class Post(models.Model):
FOOTBALL = 'F' # Variable name and db_value
CRICKET = 'C'
INTRODUCTION = 'I'
CATEGORY_CHOICES = (
(FOOTBALL, 'Soccer'), # Variable name and display value
(CRICKET, 'Cricket'),
(INTRODUCTION, 'Hello my name is'),
)
category = models.CharField(max_length=1,
choices=CATEGORY_CHOICES,
default=INTRODUCTION)
...
https://docs.djangoproject.com/en/dev/ref/models/fields/#choices
One of the advantages of this 'choice machinery' over a CharField without pre defined choices is that you are sure what values end up in your database. This lets you query them, without worrying if your data is sane:
Post.objects.filter(category=Post.CRICKET)
Use the extra table if you need the freedom to create new categories in the future. Use field choices if you don't want (or need) that freedom.
I would suggest to just add a field which makes the post relevant to that certain topic:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
type = models.CharField(max_length=20) #<--- new field: e.g 'basketball','hockey'..
def __unicode__(self):
return self.title
example query:
#basketball posts
qs = Post.objects.filter(type__icontains="basketball")
then you dont need to have multiple models which also would be redundant.
Assuming all of the posts are in the same format, you could add another field to your model like "type". Different discussion forums could send a different values for that field when the post is added.
type = models.CharField(max_length=140, choices=['Football', 'Basketball', 'Baseball'])
Storing this would make it easy to filter which posts are which.
Post.objects.filter(type = 'Football')
Assuming that one post can be about only one sport, the better approach would be to have a foreign key relation between a model that stores data about a post with another model that stores the data about sports.
Something like this
class Sport(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
sport = models.ForeignKey(Sport)
def __unicode__(self):
return self.title
This gives you the advantage of isolating the 'Sport' and the 'Post' models.You can add as many sports as you want, without any posts referring to it.
One more advantage is that you can add relevant information to the relevant models.
Eg:Suppose you want to add the information about "how many players are there in a team for sport x?". You can easily achieve this by adding a field "number_of_players" in the 'Sport' model without affecting the 'Post' model.
If you had to do this in one model, 'Post', then it would create lot of issues in terms of data consistency and other undesirable things.
Also, the query will look something like this:
posts = Post.objects.filter(sport__name = "Basketball")
PS:If your requirement is that a post can be tagged to multiple sports, then you can use ManyToMany field instead of a simple foreign key.
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
You could assign your posts tags or category, and filter on those.
If you use the model approach what happens when you add more sports? You'll need manually add the sports in your code, using a tags or category approach allows you to handle it in the db, and would then allow you to filter on the tags/categories in your system