I've been following the Django documentation "Write your app tutorial" and I keep running into the above error. It seems to be coming from this line
selected_choice = question.choice_set.get(pk=request.POST['choice'])
This is my Questions and Choices object:
class Questions(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date Published')
def __str__(self):
return self.question_text
class Choices(models.Model):
questions = models.ForeignKey(Questions, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
The code is exactly as it is on the official documentation, so I can't tell exactly where the error is coming from
"choice_set" is created as an object in Questions because the Choice model has a foreignKey relationship to Questions, so for every entry in Questions, there might be some Choice instances (rows of data in the Choice table). The general rule is a lowercase version of the model name, followed by "_set".
Your model is called Choices plural (with an 's'), so the set will probably be called "choices_set". I'm pretty sure that's the remaining problem for you.
You need to define the Choice model with a foreign key to Questions, otherwise django won't create choice_set.
Your Class name is Choices, so if you try choices_set things might work
Related
I want to add unlimited fields in the Django model.
Actually, I want my model to look like this, Is it Possible ?
It is possible, but in a somewhat different way: Namely, by creating another model (called Choice, for instance) and letting several Choice objects point to the same Question by using a ForeignKey relationship. This way there can then be arbitrarily many Choices. Django's seven-part tutorial explains precisely how to create such an app. I highly recommend to work through it start to finish: https://docs.djangoproject.com/en/4.1/intro/tutorial01/
It will exactly as picture
in admin panel
admin.py
class AnswersInline(admin.TabularInline):
model = Answers
class QuestionAdmin(admin.ModelAdmin):
inlines = [
AnswersInline
]
admin.site.register(Question,QuestionAdmin)
modelpy
class Question(models.Model):
Username = models.CharField(max_length=50, verbose_name='User Name')
Question = models.CharField(max_length=50, verbose_name='Question')
CorrectAnswer = models.CharField(max_length=50, verbose_name='Hidden Answer')
def __str__(self):
return f"{self.Question}"
class Answers(models.Model):
QuestionId = models.ForeignKey('Question', models.DO_NOTHING)
Answer = models.CharField(max_length=50, verbose_name='User Name')
def __str__(self):
return f"{self.Answer}"
I'm using enums in a django model, like:
class AwesomeNess(Enum):
slight = "SLIGHT"
very = "VERY"
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=[(tag.value, tag.name) for tag in AwesomeNess],
default=AwesomeNess.slight
)
This works fine when I use Django's filter function, like:
d = Choice.objects.filter(awesomeness=AwesomeNess.slight)
However, it does not work if I do:
choice_obj = Choice.objects.get(id=1)
choice_obj.awesomeness == AwesomeNess.slight # will return False
choice_obj.awesomeness == "AwesomeNess.slight" # will return True
Since values are stored as strings, it looks like Django forgets to cast them back to an enum when returning the data.
This gives inconsistencies when coding, since django model filters can query on the enum, while attribute equality filtering requires me to use the stringed representation of the enum.
EDIT: The enums are imported from another model class, so replacing it with Django's built-in choices class is not an option.
EDIT2: As i'm investigating this, I'm also noticing the following:
myobj = Choice.objects.get(id=1)
myobj.awesomeness # <<-- string type
myobj.awesomeness = AwesomeNess.very <<-- object changes type to enum
myobj.save()
myobj2 = Choice.objects.get(id=1) <<-- upon reload from db, the updated value is returned as string
As a novice Django user, this seems incredibly dangerous to me, and isn't mentioned in any of the "how to use enums with django" articles I've read. Is this simply due to the fact that I'm on a too old Django version?
Is there a way around this? Am I doing something very wrong? Pointers appreciated!
oh, bwt: this is on Django 2.2.24. Maybe later versions have improved enum support?
The reason that this happens is because the choices call str on the value that is given, and this thus means it will store "AwesomeNess.slight" as a string (if that is set as default).
Since django-3.0, you can work with a TextChoices class [Django-doc] which is quite the same as using an Enum. You thus can define a TextChoices model with:
class Choice(models.Model):
class AwesomNess(models.TextChoices):
SLIGHT = 'SLIGHT', 'slight'
VERY = 'VERY', 'very'
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=AwesomeNess.choices,
default=AwesomeNess.SLIGHT
)
for older versions, you need to specify that you work with the value of the AwesomeNess:
class AwesomeNess(Enum):
slight = 'SLIGHT'
very = 'VERY'
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
awesomeness = models.CharField(
max_length=255,
choices=[(tag.value, tag.name) for tag in AwesomeNess],
default=AwesomeNess.slight.value
)
I need to set up default Foreign Key for a model at the model declaration stage (in models.py). I use the following code:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return str(self.pub_date)
def create_question():
q=Question(question_text='default text', pub_date=datetime.now())
q.save()
return q.id
class Choice(models.Model):
question = models.ForeignKey(Question, default=lambda: create_question())
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
This works fine, the Choice instance and associated Question instance are created... But saving of Choice instance leads to creation of two additional (useless) Question instances. It seems like that default-lambda is called several times during Choice instance saving. I don't need these additional Question instances. How to avoid their creation?
NOTE: In python-shell everything works well:
c=Choice(votes=5, choice_text='dim')
c.save()
creates one instance of Choice and one associated instance of Question. Only using admin save button leads to extra Question instances creation!
I've got this Post model at the moment:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
If I've got different parts of a website (or a forum rather) that contain different posts, e.g. Discussion about basketball, and Discussion about football, if I wanted to return just posts concerning basketball or just posts concerning football, is the easiest way to just make a specific basketball_post model/football_post model or is there a more efficient way? Should I perhaps be storing the values differently?
Thanks
Django has a really good tutorial. It is about making a Poll app. In the first chapter the thing you want is discussed. It is about a Question that can have multiple Choices.:
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)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
The foreignKey creates a relation between two models. The same can be done for a blog:
class Category(models.Model):
title = models.CharField(max_length=200)
class Post(models.Model):
category = models.ForeignKey(Category) # This is the important part.
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
The ForeignKey relation lets you do really nice things:
basketball_posts = Post.objects.filter(category_title='Basketball')
But before we all tell you how it is done, I really recommend to do the tutorial. It introduces you to all important Django concepts: https://docs.djangoproject.com/en/1.7/intro/tutorial01/
Update
If you have a fixed set of categories that are not likely to change, than you can hardcode them and use field choices:
class Post(models.Model):
FOOTBALL = 'F' # Variable name and db_value
CRICKET = 'C'
INTRODUCTION = 'I'
CATEGORY_CHOICES = (
(FOOTBALL, 'Soccer'), # Variable name and display value
(CRICKET, 'Cricket'),
(INTRODUCTION, 'Hello my name is'),
)
category = models.CharField(max_length=1,
choices=CATEGORY_CHOICES,
default=INTRODUCTION)
...
https://docs.djangoproject.com/en/dev/ref/models/fields/#choices
One of the advantages of this 'choice machinery' over a CharField without pre defined choices is that you are sure what values end up in your database. This lets you query them, without worrying if your data is sane:
Post.objects.filter(category=Post.CRICKET)
Use the extra table if you need the freedom to create new categories in the future. Use field choices if you don't want (or need) that freedom.
I would suggest to just add a field which makes the post relevant to that certain topic:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
type = models.CharField(max_length=20) #<--- new field: e.g 'basketball','hockey'..
def __unicode__(self):
return self.title
example query:
#basketball posts
qs = Post.objects.filter(type__icontains="basketball")
then you dont need to have multiple models which also would be redundant.
Assuming all of the posts are in the same format, you could add another field to your model like "type". Different discussion forums could send a different values for that field when the post is added.
type = models.CharField(max_length=140, choices=['Football', 'Basketball', 'Baseball'])
Storing this would make it easy to filter which posts are which.
Post.objects.filter(type = 'Football')
Assuming that one post can be about only one sport, the better approach would be to have a foreign key relation between a model that stores data about a post with another model that stores the data about sports.
Something like this
class Sport(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
sport = models.ForeignKey(Sport)
def __unicode__(self):
return self.title
This gives you the advantage of isolating the 'Sport' and the 'Post' models.You can add as many sports as you want, without any posts referring to it.
One more advantage is that you can add relevant information to the relevant models.
Eg:Suppose you want to add the information about "how many players are there in a team for sport x?". You can easily achieve this by adding a field "number_of_players" in the 'Sport' model without affecting the 'Post' model.
If you had to do this in one model, 'Post', then it would create lot of issues in terms of data consistency and other undesirable things.
Also, the query will look something like this:
posts = Post.objects.filter(sport__name = "Basketball")
PS:If your requirement is that a post can be tagged to multiple sports, then you can use ManyToMany field instead of a simple foreign key.
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
You could assign your posts tags or category, and filter on those.
If you use the model approach what happens when you add more sports? You'll need manually add the sports in your code, using a tags or category approach allows you to handle it in the db, and would then allow you to filter on the tags/categories in your system
I am working through the Django tutorials, and now I am at creating a poll.
The code below works fine until I want to create choices, where for some reason I always get this error message:
line 22, in __unicode__
return self.question
AttributeError: 'Choice' object has no attribute 'question'
What am I doing wrong?
Here's my code:
import datetime
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.question
def was_published_today(self):
return self.pub_date.date() == datetime.date.today()
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
votes = models.IntegerField()
def __unicode__(self):
return self.question # this is line 22
The __unicode__ method on the Choice model should look something like:
def __unicode__(self):
return self.poll.question
question attribute does not exist on the Choice model, you need to reach for it over the poll foreign key field.
Don't forget to check out Django's great documentation that shows many examples on how to handle many to one relationships.
Edit
It would probably make more sense to return self.choice in Choice model __unicode__ method so it outputs the actual choice not the Poll question.
def __unicode__(self):
return self.choice
To follow up on rebus's answer, the tutorial actually says to add different returns to each model:
class Poll(models.Model):
# ...
def __unicode__(self):
return self.question
class Choice(models.Model):
# ...
def __unicode__(self):
return self.choice
You had 'self.question' as the return for both - I'm thinking you made the same copy/paste error I did, or the tutorial previously had that error ;-)
It should be:
def __unicode__(self):
return self.poll.question
Because poll is a related model that contains the question.
This is due to a Human Brain error or a copy/paste error. We/You thought that both functions were same and copy-pasted the same code for both, but there was one word different in both.
replace question to choice in line 22