Working with multiple forms on a single page in Django - 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 :-)

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

Django Models: One-to-Many Relationships, Reaching Across Tables -- How Can I get All Book objects with Reviews?

My Problem:
I have a handful of django models which are setup with various one-to-many relationships. I am trying to retrieve all Books which have a Review (I don't want to retrieve any books whom have no Reviews). Although what I'm trying to do seems relatively straight forward, I'm having real difficulty accomplishing my goal. It seems I may not properly understand how to reach across tables, and any advice anyone could provide in helping me better understand how to get all all Book objects which have a Review stored.
My Models:
class User(models.Model):
"""Creates instances of a `User`."""
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.CharField(max_length=50)
password = models.CharField(max_length=22)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager() # Attaches custom `UserManager` methods to our `User.objects` object.
class Author(models.Model):
"""Creates instances of a `Author`."""
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Book(models.Model):
"""Creates instances of a `Book`."""
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE) # ties us into an author for the book. if author deleted, books delete too.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Review(models.Model):
"""Creates instances of a `Review`."""
description = models.CharField(max_length=500)
user = models.ForeignKey(User, on_delete=models.CASCADE) # ties to user, if user deleted, deletes all user reviews
book = models.ForeignKey(Book, related_name="reviews") # book for review
rating = models.IntegerField() # user rating between 1-5
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = ReviewManager() # Attaches 'ReviewManager' to `Review.objects` methods.
What I've tried:
I've tried giving a related_name="reviews" to my Review.book property, and I've tried accessing reviews via Book.objects.all().reviews_set.all() or similar such queries, using _set.all() and am probably missing something / doing it incorrectly.
Desired Goal:
Retrieve all Book objects, whom have a Review attached to them (not retrieving Book objects whom have no Reviews).
Can anyone help point me in the right direction or tell me what I'm doing wrong? Thank you for your time reading!
Here's my best solution for gathering all books, whom have at least one review. This seems to be accomplishing my needs and answered my original question:
Book.objects.filter(review__gte=1).distinct()
This is saying, from Book model, get any books whom have a review gte (greater than or equal to) 1 -- and make sure they are distinct() ie, no duplicates.

What's the pros and cons of ForeignKey instead of ManyToManyField

In django tutorial it's modeled a poll structure:
from django.db import models
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, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
but I always wonder why they don't implement like ManyToMany relation:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
choices = models.ManyToManyField(Choice)
class Choice(models.Model):
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
What are the pros and cons?
I'm sure they used a ForeignKey (One To Many) relationship in the tutorial because they were trying to keep the example as simple as possible, with only two models: Question and Choice. Notice that votes is a field of the Choice model, which makes it very simple to show the results of the poll.
If you were doing a poll where you had the same choices for every question (Strongly Agree, Somewhat Agree, etc.), then a ManyToMany relationship might be appropriate. But that makes things more complicated. Your proposed models for a ManyToMany relationship were:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
choices = models.ManyToManyField(Choice)
class Choice(models.Model):
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
But this wouldn't work correctly without some changes.votes is still a field of the Choice table, but now each choice applies to many questions. You would be able to see that the Strongly Agree choice got 38 votes, but you couldn't tell which questions the voters agreed with. To be able to tabulate the votes correctly, you would have to do something like
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
choices = models.ManyToManyField('Choice', through='QuestionChoices')
class Choice(models.Model):
choice_text = models.CharField(max_length=200)
class QuestionChoices(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
votes = models.IntegerField(default=0)
Now you can tabulate exactly how many people voted for each question-choice combination, but it's more complicated and not very suitable for a getting-started tutorial.
With the ManyToMany models that you proposed, Django would automatically create the QuestionChoices model behind the scenes, but in order to attach the votes field to it you have to do it explicitly yourself.
If you have a relationship that could be modeled as either OneToMany or ManyToMany, the pros and cons depend on your particular application. In general you want to use a model that most accurately reflects the real-life situation. But you also have to consider how you need to update and summarize the data, and try to arrive at the best compromise. In my experience there aren't many situations where the choice is difficult.

Allow users to vote up, or down, or nothing

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.

Modeling django model statistics

I'm working on a questionnaire model for django that should be maintainable by someone with no programming experience, so I've spent a lot of time crafting my models to compensate for minor details.. Now I want to unlock the potential of SQL database queries to be able to generate statistics about the responses and feedback given.
One of my question types is a 5 star rating, so I would like to be able to gather statistics about the question like:
How many responses for question q were 5 star (, 4star, 3star, etc.)?
What was the average rating response?
Ideally I would like to record these statistic questions in a model, and create a view that shows all the statistics asked and keep the entire thing programmatic.
Should this be a carefully crafted model or set of models like feedback, or is there already some framework or module that handles these situations for me?
My questionnaire/models.py:
class QuestionType(models.Model):
name = models.CharField(max_length=256, blank=True, default="")
class Question(models.Model):
text = models.TextField()
type = models.ForeignKey(QuestionType)
class Response(models.Model):
question = models.ForeignKey(Question)
answer = models.TextField()
class Feedback(models.Model):
user = models.ForeignKey(User)
responses = models.ManyToManyField(Response)
response_time = models.DateTimeField(auto_now_add=True)
This would cover your requirements:
class QuestionType(models.Model):
name = models.CharField(max_length=256, blank=True, default="")
class Question(models.Model):
text = models.TextField()
type = models.ForeignKey(QuestionType)
def how_many_ratings_where_x_stars(self, stars):
return self.rating_set.filter(stars=stars).count()
def average_rating(self, stars):
return self.rating_set.aggregate(models.Avg('stars'))['stars__avg']
class Response(models.Model):
question = models.ForeignKey(Question)
answer = models.TextField()
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
class Rating(models.Model):
question = models.ForeignKey(Question)
stars = models.PositiveIntegerField(min_value=1, max_value=5)
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = [('question', 'user')]