I know how I can count things with annotate (in my view) but I would like to do the same in model (so it would be more reusable).
For example (lets take an example from django documentation) I have this model:
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class Publisher(models.Model):
name = models.CharField(max_length=300)
class Book(models.Model):
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
pubdate = models.DateField()
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
and I can use in view this:
from django.db.models import Count
pubs = Publisher.objects.annotate(num_books=Count('book'))
But how I do that in model?
I know this question is pretty basic (probably) but I'm pretty much beginner in django.
Thanks for answers!
You can use custom managers:
Django docs: Managers
class BookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(num_books=Count('book'))
class Publisher(models.Model):
name = models.CharField(max_length=300)
books = BookManager()
Now you can call it like this:
pubs = Publisher.books.all()
And you will have num_books with your objects.
You can use classmethod for this.
class Publisher(models.Model):
...
#classmethod
def get_book_count(cls):
return cls.objects.annotate(num_books=Count('book'))
You can call this method as
pubs = Publisher.get_book_count()
Edit - Also check out the answer by #Navid2zp which might be a better solution for you.
Related
I have a model for a product:
class Product(models.Model):
name = models.CharField(verbose_name=_("Name"), max_length=120)
slug = AutoSlugField(populate_from="name", verbose_name=_("Slug"), always_update=False, unique=True)
I want to have a separate model ProductFields:
class ProductFields(models.Model):
field_name = models.CharField()
field_type = models.CharField()
field_verbose_name = models.CharField()
field_max_length = models.IntegerField()
filed_null = models.CharField()
field_blank = models.BooleanField()
field_default = models.CharField()
...
So the idea is whenever I add new ProductField I want Product model to migrate that added field to its database.
For Example:
ProductFields.objects.create(field_name='description', field_type='CharField', field_verbose_name='Description', field_max_length=255, filed_null=True, filed_blank=True)
This should transform Product modal to:
class Product(models.Model):
name = models.CharField(verbose_name=_("Name"), max_length=120)
slug = AutoSlugField(populate_from="name", verbose_name=_("Slug"), always_update=False, unique=True)
description = models.CharField(verbose_name="Description", max_length= 255, null=True, blank=True)
Please let me know if you have any idea how this can be done?
If you're looking for a way to create a dynamic model you can look into these suggestions.
HStoreField using django-hstore : https://django-hstore.readthedocs.io/en/latest/
JSONField: JSONField is similar to HStoreField, and may perform better with large dictionaries. It also supports types other than strings, such as integers, booleans and nested dictionaries.https://django-pgfields.readthedocs.io/en/latest/fields.html#json-field
Or you can use a NoSQL database (Django MangoDB or another adaptation)
I am building a REST-API that will be consumed by an Angular application - this is for my guitar company’s website. There is an Artist Profile page that display an artist’s name, a short bio and a list of the projects(bands) they’re associated with and the date-ranges they were active with them. Here is where things get complicated.
Any given project can be associated with more than one artist - i.e. I could have two guitar players from the same band. I was able to solve that association by creating a many-to-many field and it worked great…until I realized that I have artists who have been in the same band at different times.
I have tried many approaches so far. I wish I could list them, but I kinda lost track. But, the code below is the where I am at right now. I can indeed associate a band with multiple artists, but I can’t associate different date ranges to different artists in the same bands. Any guidance is much appreciated.
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
def __str__(self):
string_date_range = self.begin.strftime("%d/%m/%y") + "-" + self.end.strftime("%d/%m/%y")
return string_date_range
class artistProfiles(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProjects(models.Model):
project_name = models.CharField(max_length=20)
dates = models.ManyToManyField(projectDate, related_name='date_span')
artists = models.ManyToManyField(artistProfiles, related_name='projects')
def __str__(self):
return self.project_name
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfiles, related_name='social_media', on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
artistProjects and projectDate should not be a many-to-many relationship, since a projectDate is specific to a project and unlikely to be shared by many. You can instead make artistProjects a foreign key in projectDate so that a artistProjects can have many projectDates but not vice versa:
class projectDate(models.Model):
begin = models.DateField()
end = models.DateField()
project = models.ForeignKey(artistProjects, related_name='dates')
Note that your artistProjects represents just one project, so you should avoid giving it a plural name. Naming it artistProject will make your code more readable.
Not sure whether i can solve your problems or not. I am going to describe it in simple way so you can just adjust it with your models.
These is my advice. Hope it solve your problems.
Artist Profile
id (PK)
artist_name
artist_image
description
band_website
Artist Social Media
id (PK)
artist_profile_id (FK)(Artist Profile)
facebook
twitter
instagram
Artist Project
id (PK)
artist_band_project_id (FK)(Artis Band Project)
Artist Band Project
id (PK)
begin
end
Artist Band Project Member
id (PK)
artis_band_project_id (FK)(Artist Band Project)
artis_profile_id (FK)(Artist Profile)
Regards,
Meikelwis Wijaya
#blhsing Ended up being the closest of all answers, but it took a little more massaging to get the relationships and JSON structure I was looking for.
Here is what worked for the models:
from django.db import models
class artistProfile(models.Model):
artist_name = models.CharField(max_length=20)
artist_image = models.URLField()
description = models.TextField(max_length=500)
band_website = models.URLField()
def __str__(self):
return self.artist_name
class artistProject(models.Model):
project_name = models.CharField(max_length=20)
def __str__(self):
return self.project_name
class projectTenure(models.Model):
begin = models.DateField()
# blank and null are allowed here in case an artists is still with a given project
end = models.DateField(blank=True, null=True)
project = models.ForeignKey(artistProject, on_delete=models.CASCADE)
artist = models.ForeignKey(artistProfile, on_delete=models.CASCADE,
related_name='projects')
def __str__(self):
# TODO: find a way to return the related project and artist names
string_date_range = self.begin.strftime("%d/%m/%y") + "-"
return string_date_range
class artistSocialMedia(models.Model):
facebook = models.URLField()
twitter = models.URLField()
instagram = models.URLField()
artist = models.ForeignKey(artistProfile, related_name='social_media',
on_delete=models.CASCADE)
def __str__(self):
return self.artist.artist_name
And here is how I serialized it:
from rest_framework import serializers
from .models import (artistProfile, artistProject, projectTenure, artistSocialMedia)
class artistSocialMediaSerializer(serializers.ModelSerializer):
class Meta:
model = artistSocialMedia
fields = ('facebook', 'twitter', 'instagram')
class artistProjectSerializer(serializers.ModelSerializer):
class Meta:
model = artistProject
fields = ('project_name',)
class projectTenureSerializer(serializers.ModelSerializer):
project_name = serializers.CharField(source='project.project_name')
class Meta:
model = projectTenure
fields = ('project_name', 'begin', 'end')
class artistProfileSerializer(serializers.ModelSerializer):
projects = projectTenureSerializer(many=True, read_only=True)
social_media = artistSocialMediaSerializer(many=True, read_only=True)
class Meta:
model = artistProfile
fields = ('artist_name', 'artist_image', 'description',
'band_website', 'projects', 'social_media')
class Review(models.Model):
student = models.ForeignKey(UserDetail)
text = models.TextField()
created_at = models.DateTimeField(auto_now=True)
vote_content = models.FloatField()
vote_knowledge = models.FloatField()
vote_assignment = models.FloatField()
vote_classroom = models.FloatField()
vote_instructor = models.FloatField()
class UserDetail(models.Model):
username = models.CharField(max_length=100)
email = models.CharField(max_length=255)
...
class Course(models.Model):
title = models.CharField(max_length=255)
studentlist = models.ManyToManyField(UserDetail, related_name='course_studentlist', blank=True)
reviewlist = models.ManyToManyField(Review,related_name='course_reviewlist', blank=True)
...
In the above model structure, the Course model has a relationship with UserDetail and Review with ManyToMany.
The review is based on the average of the 5 votes. (content, knowledge etc.)
A review of a course is the average of the votes of the students who take the course.
I would like to make a search and sort according to Course's review, for example, list bigger than 3 votes.
Thanks for your help.
The easiest and probably the cleanest solution is to create additional field for storing average score in Review and calculate it on save().
Is there a reason why you keep course reviews as m2m field? Do you allow the same review, with the same text etc. to be used in many courses? Maybe you need ForeignKey in this case. Then you could just do:
class Review(models.Model):
...
vote_avg = models.FloatField()
course = models.ForeignKey('Course')
...
def save(self, *args, **kwargs):
self.voce_avg = (self.vote_content + ...) / 5
super(Review, self).save(*args, **kwargs)
def foo():
return Course.objects.prefetch_related('review_set').annotate(
avg_reviews=Avg('review__vote_avg')
).filter(avg_reviews__gt=3).order_by('avg_reviews')
Try this:
from django.db.models import Count
Course.objects.annotate(reviews_count=Count('reviewlist')).filter(reviews_count__gt=3)
Using the django docs example below if i have a similar model setup it will look like this.but looking at the Album app it can be used for my other thing the problem for me will be the ForeignKey if i created a new app Photographer and what to add an Album how will i achieve this that albums mean different things for different people or context
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
class Photographer(models.Model):
# some stuff here
You can do this using simple model inheritance as follows:
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Photographer(models.Model):
# some stuff here
class Album(models.Model):
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
class PhotoAlbum(Album):
artist = models.ForeignKey(Photographer)
class MusicAlbum(Album):
artist = models.ForeignKey(Musician)
One way to relate an object to any other object is to use a generic relationship.
See: https://docs.djangoproject.com/en/1.4/ref/contrib/contenttypes/#generic-relations for an example of how to accomplish this.
Generics are nice because you can avoid concrete inheritance, unless you need it.
I have 2 models named 'Author' and 'Entry' as defined below.
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
msgtoauthor = models.TextField()
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
I am trying to access 'Author.msgtoauthor' from 'Entry'.
I know, I can retrieve the relationship between Entry and Author by
e = Entry.objects.get(authors)
Is it possible to extract the author id?
I know in the backend, Django creates a table for Authors and Entries but I want to update 'msgtoauthors' from a method in 'Entry'.
Thanks In Advance.
Did you mean
for author in my_entry.authors.all():
author.msgtoauth = 'Here is new content'
author.save()
?
Entry.authors returns a RelatedManager and my_entry.authors.all() is a QuerySet, that returns the Author objects. See https://docs.djangoproject.com/en/1.3/ref/models/relations/ and https://docs.djangoproject.com/en/1.3/topics/db/models/#many-to-many-relationships.
(Updated.)
Try this:
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
def msgtoat(self, message):
self.authors.update(msgtoauthor=message)
For example, to update an entry:
entry.msgtoat('Hello')
If all the authors get the same value you can do:
entry.authors.update(msgtoauthor='Hey there!')
https://github.com/Ry10p/django-Plugis/blob/master/courses/models.py
line 52
class name():
the_thing1 = models.CharField()
another = models.TextField()
class name2():
the_thing2 = models.ForignKey(the_thing1)
another2 = models.ForignKey(another)
-Cheers