Allow users to vote up, or down, or nothing - django

I have made an app to create article. This is the model:
class Article(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=150)
body = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
tags = models.ManyToManyField(Tag)
article_image = models.ImageField(upload_to=get_upload_file_name, blank=True)
Here I want to setup a voting system for each app. There I would like give the users the feature to vote up, or vote down, or do nothing. Boolean field would have been great, but since an article can have 3 choices of votes (vote up / vote down / or default = no vote ), how should I go about doing this? Your help and guidance will be very much appreciated. Thank you.

Related

Many foreign keys in a model, better way to do models?

I'm kind of new to django. While writing the models for an app I'm doing, I felt like I was doing something wrong because of all the foreign keys I was using for a single model (ticket model to be specific)
My thinking at the beginning was that I wanted each Ticket to keep the information on it's creator, what team this ticket is assigned to and the comments made on the ticket. And other information that don't need foreign keys. Am I doing this right ? I dont know why, but I feel like there is a better way.
class Team(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000)
members = models.ManyToManyField(User, through='Team_members')
def __str__(self):
return self.name
class Ticket(models.Model):
name = models.CharField(max_length=200)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
comments = models.ForeignKey(Comment, on_delete=models.CASCADE)
#worker = models.ForeignKey(User, on_delete=models.CASCADE) *to be finished
description = models.CharField(max_length=500)
status = models.BooleanField(default=False)
date_opened = models.DateTimeField('date opened')
date_closed = models.DateTimeField('date closed',null=True, blank=True)
def __str__(self):
return self.name
class Team_member:
team = models.ForeignKey(Team)
user = models.ForeignKey(User)
date = models.DateTimeField('date joined')
class Comment:
text = models.CharField(max_length=2000)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.text
Your question actually has very little (like, nothing?) to do with Django.
Please read about database normalization, it should answer your questions.
Regarding your models, I could notice the following:
Your models look quite good. Don't be afraid of foreign keys, they are the main reason you use a relational database :)
I assume, User might be a member of a Team (service?) or somebody who opens a ticket. If so: when you will have worker foreign key, you most likely won't need team in the Ticket model. It will be redundant, as worker would have a relation to Team.
Nitpicking: Team_member is not pythonic, a PEP8 compliant version would be TeamMember

Excluding objects from Django queryset based on recency

I have a reddit-like Django app where users can post interesting urls (links) and then publicly comment under them. The two data models to represent this are:
class Link(models.Model):
description = models.TextField(validators=[MaxLengthValidator(500)])
submitter = models.ForeignKey(User)
submitted_on = models.DateTimeField(auto_now_add=True)
class Publicreply(models.Model):
submitted_by = models.ForeignKey(User)
answer_to = models.ForeignKey(Link)
submitted_on = models.DateTimeField(auto_now_add=True)
description = models.TextField(validators=[MaxLengthValidator(250)])
How do I query for all Links which have at least 1 or more publicreply, and secondly where the latest publicreply is not by self.request.user? I sense something like the following:
Link.objects.filter(publicreply__isnull=False).exclude(**something here**)
Please advise! Performance is key too, hence the simpler the better!
For performance and simplicity you could cache both the number of replies and the latest reply:
class Link(models.Model):
...
number_of_replies = models.PositiveIntegerField(default=0)
latest_reply = models.ForeignKey('myapp.Publicreply', related_name='+', blank=True, null=True, on_delete=models.SET_NULL)
When a reply is entered, update the corresponding link.number_of_replies and link.latest_reply.
The query would then be:
Link.objects.filter(number_of_replies__gte=1)\
.exclude(latest_reply__user=request.user)

Working with multiple forms on a single page in Django

I'm new to Django and I'm having a hard time wrapping my head around how to deal with "nested" forms in a template and how to process those forms accordingly. I'm creating a polling application similar to the tutorial, but more complex. I have multiple models (Poll, Question, Choice, Vote). A poll contains many questions, a question contains many choices.
I want to allow a user to view all the questions in a poll at once and vote on each question by selecting a choice from each question's choice set. After the user selects a choice for each question, they submit all their votes at once and process them to create the vote objects.
I'm really scratching my head at how to do this. Any help would be greatly appreciated.
Here are how my models are set up:
models.py
class Poll(models.Model):
name = models.CharField(max_length=255, default="Unnamed Poll")
key = models.CharField(max_length=16, blank=True, editable=False, unique=True, db_index=True, null=True)
instructor = models.ForeignKey(User)
course = models.ForeignKey(Course)
active = models.BooleanField(default=False)
anonymous = models.BooleanField(default=True, help_text="Allow votes to be anonymous?")
class Question(models.Model):
question_text = models.CharField(max_length=255, verbose_name='Poll Question')
poll = models.ForeignKey(Poll)
class Choice(models.Model):
question = models.ForeignKey(Question)
choice_text = models.CharField(max_length=255, verbose_name='Response Choice')
class Vote(models.Model):
question = models.ForeignKey(Question)
choice = models.ForeignKey(Choice)
student = models.ForeignKey(User)
You should use django.forms.Formsets in your case.Read about formsets here
Please comment to ask for further clarifications.
cheers :-)

Why is a Django model taking so long to load in admin?

I have a fairly simple Django set up for a forum, and one of the most basic models is this, for each thread:
class Post(models.Model):
created = models.DateTimeField(auto_now_add=True)
last_reply = models.DateTimeField(auto_now_add=True, blank=True, null=True)
username = models.ForeignKey(User, related_name="forumuser")
fixed = models.BooleanField(_("Sticky"), default=False)
closed = models.BooleanField(default=False)
markdown_enabled = models.BooleanField(default=False)
reply_count = models.IntegerField(default=0)
title = models.CharField(_("Title Post"), max_length=255)
content = models.TextField(_("Content"), blank=False)
rating = models.IntegerField(default=0)
followers = models.IntegerField(default=0)
ip_address = models.CharField(max_length=255)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return "/post/%s/" % self.id
Then we have some replies:
class PostReply(models.Model):
user = models.ForeignKey(User, related_name='replyuser')
post = models.ForeignKey(Post, related_name='replypost')
created = models.DateTimeField(auto_now_add=True)
content = models.TextField()
ip_address = models.CharField(max_length=255)
quoted_post = models.ForeignKey('self', related_name='quotedreply', blank=True, null=True)
rating = models.IntegerField(default=0)
reply_order = models.IntegerField(default=1)
Now, currently there just over 1600 users, 6000 Posts, and 330,000 PostReply objects in the db for this setup. When I run this SQL query:
SELECT * FROM `forum_post` LIMIT 10000
I see that Query took 0.0241 sec which is fine. When I browse to the Django admin section of my site, pulling up an individual Post is rapid, as is the paginated list of Posts.
However, if I try and pull up an individual PostReply, it takes around 2-3 minutes to load.
Obviously each PostReply admin page will have a dropdown list of all the Posts in it, but can anyone tell me why this or anything else would cause such a dramatically slow query? It's worth noting that the forum itself is pretty fast.
Also, if it is something to do with that dropdown list, has anyone got any suggestions for making that more usable?
Try to add all foreign keys in raw_id_fields in admin
class PostReplyAdmin(ModelAdmin):
raw_id_fields = ['user', 'post', 'quoted_post']
This will decrease page's load time in change view. The problem is that django loads ForeignModel.objects.all() for each foreign key's dropdowns.
Another way is to add foreign keys in autocomplete_fields (docs) in admin
class PostReplyAdmin(ModelAdmin):
autocomplete_fields = ['user', 'post', 'quoted_post']
As pointed by #Andrey Nelubin the problem for me was indeed in the page loading all related models for each foreign key's dropdown. However, with autocomplete_fields selects are turned into autocomplete inputs (see figure below), which load options asynchronously.

Post and Comment with the same Model

I have created a simple project where everyone can create one or more Blog.
I want to use this models for Post and for Comment:
class Post_comment(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField(_('object ID'))
content_object = generic.GenericForeignKey()
# Hierarchy Field
parent = models.ForeignKey('self', null=True, blank=True, default=None, related_name='children')
# User Field
user = models.ForeignKey(User)
# Date Fields
date_submitted = models.DateTimeField(_('date/time submitted'), default = datetime.now)
date_modified = models.DateTimeField(_('date/time modified'), default = datetime.now)
title = models.CharField(_('title'), max_length=60, blank=True, null=True)
post_comment = models.TextField(_('post_comment'))
if it is a comment the parent is not null.
So in most case the text field will contain a little bit of text.
Can I use this model for both Post and Comment ?
Is it a good solution ?
Its technically possible, but sounds like a bad solution.
Most of the queries you'll run are specific to either post or comment (examples: get all posts ordered by date to show on the blog index page, get 5 most recent posts' titles to show on a widget, get 5 most recent comments to show in a "latest comments" widget, get all comments of a specific post, get all the posts that a user has posted, etc). So the cost of having them in the same table is always having the .filter(parent=None) which means less readable code and some performance loss.