I have an Article table
class Article(models.Model):
"""
Model to keep articles
"""
ext_id = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255, unique=True, db_index=True)
content = models.TextField()
summary = models.TextField()
img_url = models.URLField(max_length=200)
author = models.CharField(max_length=50, blank=True, null=True)
sport = models.ForeignKey('Sport')
posted_on= models.DateTimeField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __unicode__(self):
return "%s by %s" % (self.title, self.author)
A table where I store articles liked by a user :
class LikedArticle(models.Model):
"""
Articles that a user wants to read
"""
ext_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
article = models.ForeignKey(Article)
profile = models.ForeignKey(Profile)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
and unliked :
class UnlikedLikedArticle(models.Model):
"""
Articles that a user does not want to read
"""
ext_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
article = models.ForeignKey(Article)
profile = models.ForeignKey(Profile)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Now here, both the tables liked and unliked, are structurally the same.
I find it better to store it like this instead of storing say a bool field called is_liked because I exactly know what data I am storing. So I don't have to query a huge set of articles when I know that I am only interested in LikedArticle.
Is this the correct approach ? I am only confused because structurally they look the same and something doesn't feel right about this design.
the best approach that i recommend is to use one table and add is_liked field. (and add index to this field, so you get high performance queries)
but if still you want to use your approach with 2 table, then you need to fix your design.
use one abstract model that has all fields, and the Like and Unlike tables inherit from the abstract model
class ActionOnArticle(Model):
your fields here..
class Meta:
abstract = True
class LikedArticle(ActionOnArticle):
class UnLikedArticle(ActionOnArticle):
I think is_liked is not a good option if you want to save other information per profile, like that: who liked what and when and so on. If you want to lose those info so my suggestion is to use many to many relationship and the article model will be something like that:
class Article(models.Model):
"""
Model to keep articles
"""
ext_id = models.UUIDField(primary_key=True, db_index=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255, unique=True, db_index=True)
content = models.TextField()
summary = models.TextField()
img_url = models.URLField(max_length=200)
author = models.CharField(max_length=50, blank=True, null=True)
sport = models.ForeignKey('Sport')
posted_on= models.DateTimeField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
likes = models.ManyToManyField(Profile)
unlikes = models.ManyToManyField(Profile)
def __unicode__(self):
return "%s by %s" % (self.title, self.author)
https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_many/
While if you want to save the info mentioned at the beginning of my reply, I think #Eyal answer is fine
I'd use the "is_liked" BooleanField and filter on it to just get the liked or disliked articles. Filtering on a BooleanField (add db_index=True on the field options) will be extremely fast with any decent database so you are very unlikely to get a noticable performance increase using separate tables even if they were huge.
Related
I have two different models. HitCount model stores IP addresses whose was viewed Post. And what i want is filtering popular 3 posts which viewed more. I've tried some queries but i couldn't. I am sharing my models with you.
class Post(ModelMeta, models.Model):
title = models.CharField(max_length=255, verbose_name='Başlık', unique=True)
slug = models.SlugField(max_length=255, unique=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='blog_posts', verbose_name="Yazarı")
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='blog_posts',
verbose_name="Kategorisi", null=True)
tags = models.ManyToManyField(Tag, related_name='blog_posts', verbose_name='Etiketler')
image = models.ImageField(verbose_name='Fotoğraf (800x460)')
content = RichTextField()
description = models.TextField(null=True)
status = models.IntegerField(choices=STATUS, default=0, verbose_name='Yayın Durumu')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Oluşturulma Tarihi')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Güncellenme Tarihi')
#property
def get_hit_count(self):
return HitCount.objects.filter(post=self).count()
class HitCount(models.Model):
ip_address = models.GenericIPAddressField()
post = models.ForeignKey("Post", on_delete=models.CASCADE)
def __str__(self):
return f'{self.ip_address} => {self.post.title}'
You can try something like this :
most_viewed_posts = Post.objects.all().order_by('-get_hit_count')[3]
I don't think that you can order by 'get_hit_count', but I think those questions can help you : Django order_by a property
Using a Django custom model method property in order_by()
I did what i want with sorted method. Thanks Alexandre Boucard for the resources.
Solution;
sorted(Post.objects.filter(status=1), key=lambda a: a.get_hit_count, reverse=True)
reverse=False as a default and it sorts ascending in this case i want to get reversed so i used reverse=True
well my college is making us go learn a framework and make a website with it in a month, and it's really killing me, because of that I couldn't really get a good understanding of the Django framework as I am making progress while watching YouTube vids and reading docs.
Anyways my models are all messed up which made the job even harder, and whenever I solve a problem another one arises, but the deadline is close and making any changes to the models will cost me a great deal of time. This time my problem is about fetching data.
The concerned models are the following:
The User class for authentication
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True,null=False)
username = models.CharField(max_length=50)
email = models.EmailField(unique=True)
nom = models.CharField(max_length=255)
prenom = models.CharField(max_length=255)
usertype = models.CharField(choices=types,max_length=20,default="user")
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
is_superuser = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
student_data = models.OneToOneField(Etudiant, on_delete=models.CASCADE,blank=True, null=True,related_name='Etudiant_access')
Prof_data = models.OneToOneField(Prof, on_delete=models.CASCADE,blank=True, null=True)
objects=UserManager()
def __str__(self):
return self.prenom + " " + self.nom
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
The Students(Etudiant) class for managing the students:
class Etudiant(models.Model):
filiere = models.ForeignKey(Filiere, on_delete=models.DO_NOTHING)
classe = models.ForeignKey(Classe,null=True, on_delete=models.DO_NOTHING)
notes = models.ManyToManyField(note,blank=True, null=True)
The class Classe (LMAO) for managing the different classes:
class Classe(models.Model):
#Cla_id = models.AutoField(primary_key=True, null=False)
Designation = models.CharField(max_length=100)
filiere = models.ForeignKey(Filiere, on_delete=models.CASCADE)
Epreuve = models.ManyToManyField(Epreuve,blank=True, null=True)
def __str__(self):
return self.Designation
The thing is that I wanna fetch all data of the Users that are students (which means that their Prof_data attribute is blank/null and their student_data attribute is pointing to the Etudiant(Student) class while having an Etudiant.classe attribute equals to a value in the view's parameters
I've solved a great deal of it but I'm stuck at the end
This is my view function:
#login_required
def class_info(request,design):
#getting the Classe(s) from the url which Designation == design
classe_now = Classe.objects.get(Designation=design)
print(classe_now) # This works like a charm
#getting the Students objects that are part of the class_now
Etudiants = Etudiant.objects.filter(classe=classe_now)
print(Etudiants) # This works too. It returns the 'Etudiant' objects from where i wanna access to the Users data
#getting the User data of the student objects (This is where i get confused)
students_to_show = User.objects.filter(student_data=Etudiants)
pprint(students_to_show)
return render(request, 'Prof/class_info.html')
I am really confused, you are truly my last hope in this, and thank you for your time.
You can filter your User model by selecting all users that do have empty/null relation to Prof model and nonempty/null relation to Etudiant model.
student_users = User.objects.filter(Prof_data__isnull=True, student_data__isnull=False)
then for each stident_user, you can fetch its student data in the following manner:
student_user = student_users[0]
student_user.student_data.filiere
student_user.student_data.classe
student_user.student_data.notes
You can then pass the queryset result to the render function as a context variable. Check this brief tutorial on how to pass data to templates.
my models
class Player(TimeStampedModel):
name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
email_verified = models.BooleanField(default=False, blank=True)
phone = models.CharField(max_length=200)
phone_verified = models.BooleanField(default=False, blank=True)
company_id = models.ImageField(upload_to=get_file_path_id_card, null=True,
max_length=255)
company_id_verified = models.BooleanField(default=False, blank=True)
team = models.ForeignKey(Team, related_name='player', on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
this is my model , how to filter data in multiple model?
You can use a Queryset to filter by modal object's field.
You can use this to also filter relationships on models.
In your example, you can do a filter of all the Player entries that have a Character that have Weapon with strength > 10
Player.objects.filter(character__weapon__strength__gt=10)
You can also separate them out into 3 variables for readability purposes.
player_q = Player.objects.filter(character__isnull=False)
ch_q = player_q.filter(weapon__isnull=False)
wpn_dmg = ch_q.filter(strength__gt=10)
Please note that filters are lazy and thus don't return actual model instances untill they're evaluated. I think in this case gt returns an instance.
This documentation goes over all the fieldset lookups you can do with QuerySet object methods filter(), get(), and exclude()
Background
I'm storing data about researchers. eg, researcher profiles, metrics for each researcher, journals they published in, papers they have, etc.
The Problem
My current database design is this:
Each Researcher has many journals (they published in). The journals have information about it.
Likewise for Subject Areas
But currently, this leads to massive data duplication. Eg, the same journal can appear many times in the Journal table, just linked to a different researcher, etc.
Is there any better way to tackle this problem? Like right now, I have over 5000 rows in the journal column but only about 1000 journals.
Thank you!
EDIT: This is likely due to the way im saving the models for new data (mentioned below). Could anyone provide the proper way to loop and save hashes to models?
Model - Researcher
class Researcher(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
scopus_id = models.BigIntegerField(db_index=True) # Index to make searches quicker
academic_rank = models.CharField(max_length=100)
title = models.CharField(max_length=200,default=None, blank=True, null=True)
salutation = models.CharField(max_length=200,default=None, blank=True, null=True)
scopus_first_name = models.CharField(max_length=100)
scopus_last_name = models.CharField(max_length=100)
affiliation = models.CharField(default=None, blank=True, null=True,max_length = 255)
department = models.CharField(default=None, blank=True, null=True,max_length = 255)
email = models.EmailField(default=None, blank=True, null=True)
properties = JSONField(default=dict)
def __str__(self):
return "{} {}, Scopus ID {}".format(self.scopus_first_name,self.scopus_last_name,self.scopus_id)
Model - Journal
class Journal(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
researchers = models.ManyToManyField(Researcher)
title = models.TextField()
journal_type = models.CharField(max_length=40,default=None,blank=True, null=True)
abbreviation = models.TextField(default=None, blank=True, null=True)
issn = models.CharField(max_length=50, default=None, blank=True, null=True)
journal_rank = models.IntegerField(default=None, blank=True, null=True)
properties = JSONField(default=dict)
def __str__(self):
return self.title
How I'm currently saving them:
db_model_fields = {'abbreviation': 'Front. Artif. Intell. Appl.',
'issn': '09226389',
'journal_type': 'k',
'researchers': <Researcher: x, Scopus ID f>,
'title': 'Frontiers in Artificial Intelligence and Applications'}
# remove researchers or else create will fail (some id need to exist error)
researcher = db_model_fields["researchers"]
del db_model_fields["researchers"]
model_obj = Journal(**db_model_fields)
model_obj.save()
model_obj.researchers.add(researcher)
model_obj.save()
Here is how it works :
class Journal(models.Model):
# some fields
class Researcher(models.Model):
# some fields
journal = models.ManyToManyField(Journal)
Django gonna create a relation table :
Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship
So you'll have many rows in this table, which is how it works, but journal instance and researcher instance in THEIR table will be unique.
Your error is maybe coming from how you save. Instead of :
model_obj = Journal(**db_model_fields)
model_obj.save()
Try to just do this:
model_obj = Journal.objects.get_or_create(journal_id)
This way you'll get it if it already exists. As none of your fields are unique, you're creating new journal but there's no problem cause django is generating unique ID each time you add a new journal.
I want to find the number of articles for which a specific user has created articlehistory records.
The models for that look like this:
class Article(models.Model):
"""The basic entity of this app.)"""
documentID = models.CharField(blank=True, max_length=1000)
cowcode = models.IntegerField(blank=True, null=True)
pubdate = models.DateField(default=datetime.datetime.today)
headline = models.CharField(blank=True, max_length=1500)
source = models.CharField(blank=True, max_length=5000)
text = models.TextField(blank=True, max_length=1000000)
assignments = models.ManyToManyField(Assignment)
class Meta:
ordering = ['pubdate']
def __unicode__(self):
return self.headline
class ArticleHistory(models.Model):
"""(Modelname description)"""
article = models.ForeignKey(Article, related_name='Article History')
coder = models.ForeignKey(User, related_name='Article History')
last_updated = models.DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
return self.last_updated
The way I'm trying to do this at the moment is like this:
assignment.finished_articles = Article.objects.filter(cowcode=country).filter(pubdate__range=(start_date,end_date), articlehistory__coder=request.user.id).count()
This doesn't work, however and exhibits another weird behaviour:
I try to do this:
for assignment in assignments:
country = assignment.country.cowcode
start_date = assignment.start_date
end_date = assignment.end_date
articles = Article.objects.filter(cowcode=country).filter(pubdate__range=(start_date,end_date)).select_related()
assignment.article_num = articles.count()
#assignment.finished_articles = Article.objects.filter(cowcode=country).filter(pubdate__range=(start_date,end_date), articlehistory__coder=request.user.id).count()
This works fine, unless I try to include finished_articles, then article_num gets shortened to one result.
It would be really great if anyone has a pointer to who to solve this.
Make use of reverse relation of ForeignKey created by parameter related_name:
Rename attribute related name to "article_history_set".
Now, it gives you easy pointer: user.article_history_set is a set of Article History objects where coder is set to this user.
Then you can find which article it is related to by doing article_history.article.
At the end, you have to get rid of repetition and get length of that list.
Here you have more about related_name attribute: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name