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
Related
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()
class Cities(models.Model):
city_main_image = models.FileField()
city_name = models.CharField(max_length=200)
city_info = models.CharField(max_length=1000)
city_images = models.FileField()
In my models.py I have Cities class and I want to upload several images for this class variable, to be clearly for city_images variable how can I do this? Are there any way to do this or not?
Few notes about your code before answering your question.
1) Stick to singular model names, City rather than Cities.
2) Don't repeat the model name in every field, so you get "main_image",
"name", "info", "images".
3) Use ImageField for images rather than FileField.
4) No need to have 2 fields for main_image and images. You can add an extra field to make the image a main image or not.
Now, to answer your question, you need to read about relations in an SQL database.
To use relations between your models with django's ORM, look at https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey
You have 2 options: ForeignKey or ManyToManyField. Stick with the ForeignKey as you don't need a many to many relation.
So you'll have something like the following:
class City(models.Model):
...
class CityImage(models.Model):
image = models.ImageField(...)
city = models.ForeignKey(City) # you might need to add on_delete parameter depending on your django version.
or
class CityImage(models.Model):
image = models.ImageField(...)
class City(models.Model):
...
images = models.ManyToManyField(CityImage)
class Cities(models.Model):
city_main_image = models.FileField()
city_name = models.CharField(max_length=200)
city_info = models.CharField(max_length=1000)
class CityImages(models.Model):
city_id = models.ForeignKey(Cities)
city_images = models.FileField()
Now each of your city in Cities can have one or more images in another model called CityImages. If we talk in terms of tables then the primary key for a row in table Cities would be associated to one or more rows in table city_images. I will strongly suggest you to go through official introductory tutorial of django. Also I personally find this tutorial very helpful for beginners. Just in case it helps.
One way you could do this is to make a CityImage model, which would allow you to make a ForeignKey to the City model, with a related_name=images for the reverse lookup.
class City(models.Model):
name = models.CharField(max_length=200)
info = models.CharField(max_length=1000)
#property
def main_image(self):
try:
return self.images.get(primary=True)
except CityImage.DoesNotExist:
pass
except CityImage.MultipleObjectsReturned:
# Handle this case, the application should ensure only one is set to `True` at a time...
class CityImage(models.Model):
city = models.ForeignKey(City, related_name='images')
primary = models.BooleanField(default=False)
image = models.FileField()
Say I have Django models like so:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
authors = models.ManyToManyField(Author)
What's the best way to get the the number of books each author created (preferably ordered) within a query or two?
The Author set might get pretty big, so I'm looking for a way to avoid iterating over all of it.
in your model book add...
...
author = models.ForeignKey(Author)
...
and to get then you use a QuerySet like
...
Author.objects.get(id=some_id).book_set.all() # this line, return all book of these author, if you want get the number just add at end '.count()'
...
I'm trying to find a good tutorial for django how to create multiple tags in a model.
For example:
class Tag(models.Model):
name = models.CharField()
class Sample(models.Model):
name = models.CharField()
urlA = models.CharField()
urlB = models.CharField()
tagA = models.ManyToManyField(Tag)
tagB = models.ManyToManyField(Tag)
I would like to display the tags as an input field and separate by ',' and split in the save method. So I'd like to see 2 different input for the 2 tag field.
If you have an easy way to do or know a good tutorial, please tell me! :)
Thank you guys!
Edit: you do not have to have the actual table sets over laid. You can generate any queryset you want to inn your views. Your url conf can be set up to display the detail view from multiple url. If i am still not understanding then please refine your question.
For having multiple anything tags categories your either going m21 or m2m. So when you create your tags you can add them one by one. Are you familiar with what the Django ORM has to offer with some of its admin functionality? Please give the documentation a good look through. Your approach to this problem is anything but reasonable. Not trying to rub you the wrong way I'm no genius. You would do something like so.
class Tag(models.Model):
title = models.CharField(max_length=250, blank=True)
slug = models.SlugField(blank=True
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
ordering = ['title']
#models.permalink
def get_absolute_url(self):
return "/tags/%s/" % self.slug
class Entry(models.Model):
title = models.CharField(max_length=250, blank=True)
body = models.TextField()
tags = models.ManyToMany('Tag')
slug = models.SlugField()
#models.permalink
def get_absolute_url(self):
return "/blog/%s/" % self.slug
There's a little more code to be done for the EntryAdmin and the TagAdmin models, Many other things that can be done as well. I am not sure what you are trying to achieve with that if you could be more clear? Thank you, the above is a rough illustration of how I would approach it.
I found a solution from here:
https://dev.to/thepylot/how-to-add-tags-to-your-models-in-django-django-packages-series-1-3704
django-taggit is very useful for tagging.
It is
a reusable application that primarily offers you a Tag model, and a manager for easily adding tags to any model.
pip install django-taggit
After that, open settings.py and edit the installed apps section:
INSTALLED_APPS = [
...
'taggit'
]
After that, edit your model and add tags like this:
tags = TaggableManager()
The TaggableManager will show up automatically as a field in a ModelForm or in the admin.
Documentation: https://django-taggit.readthedocs.io/en/latest/index.html
I have this setup in my models:
class Author(models.Model):
name = models.CharField(max_length=100)
class Topic(models.Model):
name = models.CharField(max_length=100)
class Article(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, null=True, blank=True)
topics = models.ManyToManyField(Topic, null=True, blank=True)
Given an author, I want to know which topics he wrote about:
def author_info(request, pk):
author = get_object_or_404(Author, pk=pk)
topics = ????
If I had specified a through field, I could use that, but now Django makes the through field for me, and since its supposed to be transparent, Id rather not reference the field (unless there is a proper Django construction for that).
Use Lookups that span relationships:
topics = Topic.objects.filter(article__authors=author).distinct()
Note: you have to use distinct here, because the same topic can be selected by different articles.