Currently I am creating a site that I would like users to be able to comment in. I am just having a hard time wrapping my head around how to model the models so to speak. The way I have it, I believe that the comments aren't connecting themselves to the main article, I believe they just get added and sunk into the abyss. What's the best way to connect the models? Should I just not have a separate model for the comments? This is more of a hypothetical question than a how to code question. Currently this is the way I have it.
class Comments(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
comment = models.CharField(null=True, blank=True, max_length=5000)
date = models.TextField(blank=True, null=True)
username = models.CharField(null=True, blank=True, max_length=20)
class Article(models.Model):
title = models.CharField(max_length=250, primary_key=True)
content = models.TextField(blank=True, null=True)
usedfor = models.TextField(blank=True, null=True)
url=models.CharField(max_length=200, null=True)
username = models.CharField(max_length=50, null=True, blank=True)
article_programming_language = models.ForeignKey(ProgrammingLanguage, on_delete=models.CASCADE, blank=True, null=True)
score = models.IntegerField(max_length=5, null=True, blank=True)
article_framework = models.ForeignKey(Framework, on_delete=models.CASCADE, related_name="article_framework", blank=True, null=True)
date_added = models.IntegerField( max_length=10, blank=True, null=True)
article_comments = models.ForeignKey(Comments, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.title
It seems like that you have got all the things you need in your class.Here's some opinions.
Comments should be a separate model.
Each comment should be addressed to a specific user (or an anonymous user).A user could have many comments, but a comment should only be created by one user.Adding a ForeignKey inside comments as [Bruno Monteiro] advised could help.
Each comment should be addressed to a specific article.This could also be an One-To-Many Relationship(An article could have many comments,but a comment only belongs to a specific article).
'Best design' is too huge to answer.The design that meets your need is the best.So when you want to find a 'best design', just ask yourself what functions it will provide, how the comments should work,etc.
This is my suggestion,
Create a comment model,
Add user model with a f.k. user = models.ForeignKey(User, on_delete=models.CASCADE,) # You can add null and blank as per your requirement
Add article model with a f.k., article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='articlecomment')
Add date with auto_now_add = True
Here users can add any number of comments they want.
Related
A little bit of info. I want the user to be able to view their feed which contains friends' posts, group posts, etc. I am using Django Rest Framework and providing this feed endpoint to a frontend.
My initial thought was just making separate models(tables) per items I needed. Like a UserPost, GroupPost, EventPost, but I feel when trying to consolidate the data it will just be doing a lot of joins and having to stitch together the data.
ex)
class UserPost(models.Model): # GroupPost, EventPost
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
# group/event = models.ForeignKey(Group/Event, on_delete=models.CASCADE)
This didn't seem like a good approach in case we wanted to add post type functionality for other models.
My other approach is using intermediate models. Post model is the base and UserPost, GroupPost, EventPost being the intermediate models having a OneToOne relationship to post and GroupPost would have a foreign key (OneToMany) relationship to a Group model.
ex)
class Post(models.Model):
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
class UserPost(UUID_PK, models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)
class Group(models.Model):
name = models.CharField(max_length=64)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
members = models.ManyToManyField(settings.AUTH_USER_MODEL)
class GroupPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
class Event(models.Model):
name = models.CharField(max_length=64)
about = models.TextField(null=True, blank=True)
event_date = models.DateTimeField(null=True, blank=True)
invited = models.ManyToManyField(settings.AUTH_USER_MODEL)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_events')
class EventPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_ePost', on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
This approach isn't too bad, but it does require an extra query now.
Then to mimic getting a user's "feed" I would filter like this
users = # friends
groups = # groups of the user
events = # events of the user
posts = Post.objects.filter(Q(created_by__in=users, _gPost__isnull=True, _ePost__isnull=True) | Q(
_gPost__group__in=groups) | Q(_ePost__event__in=events)).distinct().select_related('created_by')
# _gPost__isnull=True and _ePost__isnull=True is exclude the other types of post and only get the "normal" posts.
which just looks awful.
I am unsure if this is a good enough approach or if anyone else has recommended for improving this.
I did look into GenericRelationship and wasn't sure if that would actually make this better. I have a few models that deal with GenericRelationship and for the most part, have been a bit of a pain.
In my database I have user objects with two many to many fields (messages and following) on them that both contain a many to many field related to another object Topic.
class User():
messages = ManyToManyField('Message', related_name='users', blank=True, null=True)
following = ForeignKey('Following', related_name='users', blank=True, null=True)
class Message():
date = DateField(blank=True, null=True)
content = TextField(blank=True, null=True)
topics = ManyToManyField('Topic', related_name='messages', blank=True, null=True)
class Following():
name = CharField(max_length=255, blank=True, null=True)
description = CharField(max_length=255, blank=True, null=True)
topics = ManyToManyField('Topic', related_name='following', blank=True, null=True)
class Topic():
name = CharField(max_length=255, blank=True, null=True)
source = CharField(max_length=255, blank=True, null=True)
I want to filter for all "users" who have "messages" attached to them that do not contain all of the topics attached to the "following" objects on the user.
Right now I am using a loop to accomplish this:
users = set()
for user in User.objects.filter(messages__isnull=False, following__isnull=False).iterator():
if not set(user.following.values_list('topics', flat=True))
).issubset(set(user.messages.values_list('topics', flat=True)):
users.add(user.pk)
Is there a way to accomplish the same thing with a single query?
---- EDIT ----
What I have is this:
User.objects.filter(following__isnull=False
).annotate(following_count=Count('following__topics', distinct=True)
).filter(following__topics__exact=F('message__topics')
).annotate(missing_topics=ExpressionWrapper(
F('following_count') - Count('message__topics', distinct=True),
IntegerField())
).filter(missing_topics__gt=0)
If there is a better way to do this or there are reasons why I should most definitely not do it this way, what are they?
---- EDIT ----
This question helped me to understand and use HÃ¥ken Lid's answer
This is my new model and my new query:
class User():
messages = ManyToManyField('Message', related_name='users', blank=True, null=True)
following = ManyToManyField('Topic', through='Following', related_name='users', blank=True, null=True)
class Message():
date = DateField(blank=True, null=True)
content = TextField(blank=True, null=True)
topics = ManyToManyField('Topic', related_name='messages', blank=True, null=True)
class Following():
name = CharField(max_length=255, blank=True, null=True)
description = CharField(max_length=255, blank=True, null=True)
user = ForeignKey('User', related_name='following', blank=True, null=True)
topic = ForeignKey('Topic', related_name='following', blank=True, null=True)
class Topic():
name = CharField(max_length=255, blank=True, null=True)
source = CharField(max_length=255, blank=True, null=True)
User.objects.filter(~Q(messages__topics__in=F('following'))
).values('id').annotate(missing_topics=Count('following__topics', distinct=True))
This should be possible using a subquery.
First, make sure Following.topics uses a different related name than Messages.topics.
class Following(models.Model):
topics = ManyToManyField('Topic', related_name='following')
Then it should be possible to create a subquery. Something like this:
from django.db.models import OuterRef, Subquery
user_following_topic = Topic.objects.filter(following__users=OuterRef('pk'))
User.objects.exclude(messages__topics__in=Subquery(user_following_topics.values('pk')))
This might not work and give you your expected output exactly as written, but I think the principle should work for your case as well.
On the other hand, I don't really understand your database structure. It seems you use m2m relations where foreign keys could be more appropriate and simpler. The more complicated your relations are, the harder it is to create this kind of advanced query. And queries with lots of database joins can be very slow, since they might have to process huge amounts of data, compared to simple queries.
For example, instead of using m2m realitions, Following would make more sense to me like so:
class Following():
topic = ForeignKey('Topic', on_delete=models.CASCADE)
user = ForeignKey('User', on_delete=models.CASCADE)
client = models.CharField(max_length=255, blank=True, null=True)
duration = fields.DateRangeField(blank=False, null=False)
So basically a "through" model, as explained in the django docs on model relationships where there's a similar example.
I am trying to learn django by creating a blog on my own, and I've tried some real simple steps before, but now I want to make something slightly more complex. Currently, I am thinking of dividing the blogs' 'Stories' into 'Blocks'. My idea is to have two child classes 'TextBlock' and 'ImageBlock', and my current models look like this.
class Story(models.Model):
writer = models.CharField(max_length=189)
title = models.CharField(max_length=189)
class Block(models.Model):
story = models.ForeignKey(Story, related_name='+', on_delete=models.CASCADE)
block = EnumField(choices=[
('TXT', "text"),
('IMG', "image"),
])
serial = models.IntegerField(default=0)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
class TextBlock(Block):
type = models.CharField(max_length=128, blank=True, null=True, default='paragraph')
content = models.TextField(blank=True, null=True)
class ImageBlock(Block):
src = models.URLField()
type = models.CharField(max_length=128, blank=True, null=True, default='full')
title = models.CharField(max_length=189, blank=True, null=True)
photographer = models.CharField(max_length=128, blank=True, null=True)
What I'd like to do now is to create blog entries from the django admin interface. Is it possible to create both types from the main Block? Or do I need to go to both TextBlock and ImageBlock? Any ideas on how I should proceed from here? Thanks.
I'm having a hard time aggregating project profit, so I can do more calculation with the result.
total_potential_profit = Project.objects.filter(created__year=year, created__month=month, client_sub_org__lead_contact__account_handler=user_id).aggregate(Sum('profit_aux'))
and this return None as a result, I'm not shure If I'm doing the right thing with aggregation, it seems that annotation is more appropriate in this filter, but that is also returning None as a result.
The model that I'm filtering on is
class Project(models.Model):
client_sub_org = models.ForeignKey(ClientSubOrganization)
profit_aux = models.DecimalField('profit aux', max_digits=20 decimal_places=2, default=0, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
and the user_id that I'm pulling out is from these models, client sub organization:
class ClientSubOrganization(models.Model):
lead_contact = models.OneToOneField(LeadContact, blank=True, null=True)
and Lead Contact
class LeadContact(models.Model):
account_handler = models.ForeignKey(User, blank=True, null=True, related_name='handling_leads', on_delete=models.SET_NULL)
What would be the proper way of doing this?
I am developing a small app which stores the advertisement info. My model is like this:
class Advertising(models.Model):
image_rect_url = models.URLField(blank=True, null=True,
validators=[validators.URLValidator])
image_sqre_url = models.URLField(blank=True, null=True,
validators=[validators.URLValidator])
clickthrough_url = models.URLField(blank=True, null=True,
validators=[validators.URLValidator])
ads_copy = models.TextField(blank=True, null=True)
vstart = models.DateTimeField(blank=True, null=True)
vend = models.DateTimeField(blank=True, null=True)
clicked = models.PositiveIntegerField(default=0, blank=True, null=True)
viewed = models.PositiveIntegerField(default=0, blank=True, null=True)
Now when the user cilcks the clickthrough url he shuold be redirected to that advertisement.I need a basic idea how to implement this feature.The above model only stores the information of a particular advertisement and the number of times that add has been viewed and clicked will be the number of times the clickthrough url was clicked. Any ideas on implementing this feature?
Thank you.
Something similar to this should do it
def view_for_redirect(request, id):
redirect_url = Advertising.objects.get(id=id)
return HttpResponseRedirect(redirect_url.clickthrough_url)
I have omitted all of the checks you should do to see if it not None, just FYI.
Also, I really recommend the documentation for these kinds of things, it's a quick google and it's always the first hit.