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()'
...
Related
In my Django project I have a model for products that look like this:
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Product(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# .favorite_set: ManyToOne relation coming from the
# 'Favorite' class (shown a couple of lines below)
My site's User(s) can mark some products as Favorite. To provide this functionality, I have a Django model that looks like this:
class Favorite(models.Model):
user = models.ForeignKey(User)
product = models.ForeignKey('Product')
class Meta:
unique_together = ('user', 'product',)
In that model, the .product ForeignKey creates a reverse relation in the Product model called favorite_set. That's all good and useful: When I get an HTTP request from a user to retrieve products, I can easily figure out whether it's been favorited by a particular user or not by doing this:
product = Product.objects.get(id='whatever_id')
is_favorited = bool(product.favorite_set.filter(user=self.user).count() == 1)
# or probably:
# is_favorited = product.favorite_set.filter(user=self.user).exists()
#
Now, I have another model that is heavily denormalized (SQL denormalized, that is) that I want to use for fast text searches.
This model "pretends" to be a Product, but includes data found through the "regular" Product's FK relationships into the model itself. Something like this:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
This class has its own id field (since it's a Django model) and, as you can see above, it is going to have a OneToOne relationship to the products (one of this ProductSearch entries is linked to one and only one Product)
Thanks to this model, if I want to search products whose manufacturer is "Ford" (for example), I don't need to join the Product table with the Manufacturer's table. I can do the lookup directly in ProductSearch and save a few milliseconds.
Since the ProductSearch is intended to be compatible with a Product, I'm also trying to model the favorite_set that occurs "naturally" in my Product class into this ProductSearch model.
And that's where the difficulties arise: I don't know how to do that :-)
I ideally would have something like:
class ProductSearch(models.Model):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
manufacturer_name = models.CharField(max_length=100)
#
# Couldn't find anything to do the following:
product_favorite_set = models.ManyToOneField('Favorite',
through_fields=('product',))
But I haven't been able to do that.
I have tried to "abuse" the ManyToManyField like this:
class ProductSearch(BaseModel):
product = models.OneToOneField('Product',
on_delete=models.CASCADE,
related_name='searcher')
product_name = models.CharField(max_length=100)
manufacturer_name = models.CharField(max_length=100)
product_favorite_set = models.ManyToManyField('Favorite', related_name='+',
through='Favorite',
through_fields=['product']
)
But that produces an error on System Check:
api.Favorite: (fields.E336) The model is used as an intermediate model
by 'api.ProductSearch.product_favorite_set', but it
does not have a foreign key to 'ProductSearch' or 'Favorite'.
api.ProductSearch.product_favorite_set: (fields.E339) 'Favorite.product'
is not a foreign key to 'ProductSearch'.
I imagine I could make the product_favorite_set a Python #property, and then do a custom query in it like:
class ProductSearch(BaseModel):
# ....
#property
def product_favorite_set(self):
return Favorite.objects.filter(product=self.product)
But I would like to know if I can do this using "pure" Django tools (only if out of curiosity)
Any help would be greatly appreciated. Thank you in advance.
I have some models, where Books can have many Authors and vice versa:
class Book(models.Model):
name = models.CharField(max_length=140)
class Author(models.Model):
name = models.CharField(max_length=140)
books = models.ManyToManyField(Book, related_name="authors")
I need to find all Books that have no author from a Book queryset, yet I can't find an appropriate .filter() or .exclude() clause that will do it.
Can I do this from a queryset?
You can simply write
Book.objects.filter(author_set=None)
or, if you have an existing query
book_queryset.filter(author_set=None)
I want to add fields dynamically in Django 1.8.
Here's my models.py
class Book(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Book)
def __str__(self):
return self.name
Here's my admin.py
class AuthorAdmin(admin.ModelAdmin):
admin.site.register(Author, AuthorAdmin)
class BookAdmin(admin.ModelAdmin):
admin.site.register(Book, BookAdmin)
Here's how it looks now:
I've tried to use Inline, but I can only add a new book also.
I want to be able to add many books to one author dynamically.
Something like this:
How can I do this?
Edit:
I've changed AuthorAdmin to this:
class AuthorAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ManyToManyField: {'widget': CheckboxSelectMultiple},
}
admin.site.register(Author, AuthorAdmin)
But instead of one checkbox with "N" options, I want "N" textboxes to choose the books.
You got your cardinalities the wrong way round. As you wrote it, the relationship is "An author has one book, a book has many authors", while you obviously wanted "A book has one author, an author has many books" - or possibly "a book as one or many authors, an author has one or many books".
In the first case, you have to remove the ForeignKey in Author and add a ForeignKey to Book in Author - and use an AdminInline in Author if you want to be able to add books to an author from the AuthorAdmin.
In the second case, you want a ManyToManyField, which can be in either Book or Author...
You need to use ManyToMany field for books:
books = models.ManyToManyField(Book)
Django doc for ManyToMany.
Trying to do something pretty simple with tastypie, but cannot figure out how. Lets say I have a model Author and a mode Book with a foreign key pointing to author. And I have an Author resource. So, /api/v1/author would get me a list of authors and /api/v1/author/1 would get me details on a particular author. I want /api/v1/author/1/books to get me a list of books for this particular author. How?
Example code:
from django.db import models
from tastypie.resource import ModelResource
class Author(models.Model):
name = models.CharField(max_length=200)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('Author')
class AuthorResource(ModelResource):
queryset = Author.objects.all()
Looks like the recipe for this was actually in the cookbook, but for something so obvious, the implementation is rather awkward.
http://django-tastypie.readthedocs.org/en/latest/cookbook.html#nested-resources
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