I have been searching and looking through docs, but I want to ask and confirm for the best solution here.
Trying to define model choices.
'yes, no and not sure' choice From Radio Select
How would I define for Multiple Choices
Simple Example:
In my models.py, I have
class Property(models.Model):
name = models.CharField()
class Feature(models.Model):
YES_CHOICES = ( # example of 1, there can be only one selection
('YES', 'Yes'),
('NO', 'No'),
('NOT_SURE', 'Not Sure')
)
PARKING_CHOICES = ( # example of 2, there can be multiple selections
('GARAGE', 'Garage'),
('STREET', 'Street'),
('PRIVATE_LOT', 'Private Lot'),
('VALET', 'Valet'),
)
nearby_school = models.CharField(max_length=8, choices=YES_CHOICES)
parking_options = models. MultipleChoiceField(choices=PARKING_CHOICES)
class PropertyFeature(models.Model)
property = models.ForeignKey(Property)
feature = models.ForeignKey(Feature)
...
Are those best ways to do it?
Should I use NullBooleanField instead for yes, no , not sure question?
Is that a correct way for defining and storing for multiple choice answers? Sometimes, I see people using manytomany objects.
Just want to use the most efficient and the easiest method offered from Django.
18 months or so later, there is now a better way of dealing with choices in Django; Łukasz Langa's dj.choices. An example of its use, from the blog post introducing the project:
from dj.choices import Choices, Choice
class Gender(Choices):
male = Choice("male")
female = Choice("female")
not_specified = Choice("not specified")
class User(models.Model):
gender = models.IntegerField(choices=Gender(),
default=Gender.not_specified.id)
def greet(self):
gender = Gender.from_id(self.gender)
if gender == Gender.male:
return 'Hi, boy.'
elif gender == Gender.female:
return 'Hello, girl.'
else:
return 'Hey there, user!'
This still won't work for multiple selections, though.
Yes, NullBoolean is appropriate, but if there are more options that don't fit the profile of NullBoolean, I'm in favor of IntegerField for readability and consistency across options.
Null could intuitively mean n/a, but as you add more single choice questions, I think it's even more intuitive to use an IntegerField mapped to static variables.
Also for this type of scenario where the user will probably filter properties based on these features, it's useful not to have to special case Null in your dynamic query.
Example:
...filter(Q(nearby_school__isnull=True) | Q(nearby_school='NO')),
other_choice='SOME_CHOICE')
# vs
...filter(Q(nearby_school=Feature.NOT_SURE) | Q(nearby_school=Feature.NO)),
other_choice=Feature.SOME_CHOICE)
This ancient post still serves as a great reference:
http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/
class Feature(models.Model):
YES = 0
NO = 1
NOT_SURE = 2
SOMETIMES = 3
YES_CHOICES = (
(YES, 'Yes'),
(NO, 'No'),
(NOT_SURE, 'Not Sure'),
(SOMETIMES, 'Sometimes'), # extensible.
)
As for a multiple choice field, I do think using a m2m field is the easiest/best way.
You could set up your forms.MultipleChoiceField to store data as a comma separated field & display appropriately, but the fact that you can query the m2m field easily is a huge benefit + it works right out of the box with ModelMultipleChoiceField.
Related
In the official Django 2 tutorial I found this:
from django.db import models
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
YEAR_IN_SCHOOL_CHOICES = (
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
)
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
Now my question is does using choices mean that only either of the four defined values is valid for this field? If so what is the use of specifying the max_length? If not, why not use a validator that validates if the value is actually exacly one of the specified ones or at least a validator that only accepts an specific length not just an upper bound.
The max_length is enforced at the database level, but the choices are enforced at python code level (when calling full_clean() or clean_<fieldname>()).
They are independent of each other.
If you set a value to your field other than the specified choices and you don't call instance.full_clean() or instance.clean_<fieldname>(), it could still get saved to the database without raising errors.
But if you use djangos forms, the choices validation is done for you (the form calls full_clean()) and you don't need to worry about it.
This means, for example, if you set max_length smaller than your largest option in choices, your database will silently truncate the values for that field or raise a DatabaseError; either way you will not get it to work.
This separation is useful, for example, if you want to add more choices later; if the new options are not larger than max_length, there will be no need to to change the database structure (that means, the new migration will NOT issue alter table SQL statements).
I ran into this issue recently, except that I wasn't just using a two letter code so it was a bit tedious to make sure I had a valid max_length. I ended up doing something like this:
year_in_school = models.CharField(
max_length=max(len(v[0]) for v in YEAR_IN_SCHOOL_CHOICES),
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
If I ever add an option that exceeds the existing max length, makemigrations will detect that and add that change to the migration.
I found it convenient to extend TextChoices with a property that can be used to set max_length:
TextChoicesMeta = type(models.TextChoices)
class ExtendedTextChoicesMeta(TextChoicesMeta):
#property
def max_length(cls) -> int:
return max(len(value) for value in cls.values)
class ExtendedTextChoices(models.TextChoices, metaclass=ExtendedTextChoicesMeta):
pass
class PropertyType(ExtendedTextChoices):
RESIDENTIAL = "RESIDENTIAL", _("residential")
COMMERCIAL = "COMMERCIAL", _("commercial")
INDUSTRIAL = "INDUSTRIAL", _("industrial")
RAW_LAND = "RAW_LAND", _("raw land")
SPECIAL_USE = "SPECIAL_USE", _("special use")
class Property(models.Model):
property_type = models.CharField(
choices=PropertyType.choices,
max_length=PropertyType.max_length,
)
I want Create Car Advertising website
and I have many list like year,brand and status
which is the best use Category OR choices with list
and Taking into account the I wanna make extended search engine
see code for tow methods
YEARS = (
("1990", "1990"),
("1991", "1991"),
("1992", "1992"),
.
.
.
.
("2013", "2013"),
)
class Whatever(models.Model):
# Show a list with years
birthdate = models.IntegerField(max_length=2, choices=YEARS)
#OR this method
class ChoiceYears(models.Model):
type = models.CharField(max_length=60)
def __unicode__(self):
return '%s' % self.typeclass Adv(models.Model):
class Adv(models.Model):
years = models.ForeignKey(ChoiceYears)
and this
class ChoiceStatus(models.Model):
type = models.CharField(max_length=60)
def __unicode__(self):
return '%s' % self.type
class Adv(models.Model):
status = models.ForeignKey(ChoiceStatus)
#OR this method
STATUS = (
(1, u'new'),
(2, u'old'),
)
class Adv(models.Model):
status = models.IntegerField(u'??????', choices=STATUS, default=1,)
Using choices is appropriate when the items are virtually static: they don't change or don't change often and don't need to "do" anything on their own.
Use ForeignKey when the "choices" for that field are dynamic (could change at any moment or at a whim) or you need to associate additional data with those "choices".
However, for your purposes, both "years" and "status" are good candidates for using choices. There's only ever a certain defined number of car "statuses": new, used, etc. Years wouldn't be appropriate as a model of its own, so using choices is a good idea there too. However, I'd change it to something like:
YEAR_CHOICES = [(y, y) for y in range(1990, datetime.now().year+2)]
Where "1990" is the year you want to start with. datetime.now().year gets you the current year, and since range is not end-inclusive (it returns up to but not the last number) and you seem to be dealing with model years here (which are 1 greater than the current year), you have to increment it by total of 2.
Why do u want to define a foreign key relation when choices does the job for you? I would go with the choices way
I'm new to django and I think this is a simple question -
I have an intermediate class which is coded as follows -
class Link_Book_Course(models.Model):
book = models.ForeignKey(Book)
course = models.ForeignKey(Course)
image = models.CharField(max_length = 200, null=True)
rating = models.CharField(max_length = 200,null=True)
def __unicode__(self):
return self.title
def save(self):
self.date_created = datetime.now()
super(Link_Book_Course,self).save()
I'm making this call as I'd like to have to have all of the authors of the books (Book is another model with author as a CharField)
storeOfAuthorNames = Link_Book_Course.objects.filter(book__author)
However, it doesn't return a querySet of all of the authors, in fact, it throws an error.
I think it's because book__author has multiple values- how can I get all of them?
Thanks!
I don't think you're using the right queryset method. filter() filters by its arguments - so the expected usage is:
poe = Author.objects.get(name='Edgar Allen Poe')
course_books_by_poe = Link_Book_Course.objects.filter(book__author=poe)
It looks like you're trying to pull a list of the names all the authors of books used in a particular course (or maybe all courses?). Maybe you're looking for .values() or values_list()?
all_authors_in_courses = Link_Book_Course.objects.values_list(
'book__author', flat=True
).distinct()
(Edit: Updated per #ftartaggia's suggestion)
As others already explained, the use of filter method is to get a subset of the whole set of objects and does not return instances of other models (no matter if related objects or so)
If you want to have Author models instances back from django ORM and you can use aggregation APIs then you might want to do something like this:
from django.db.models import Count
Author.objects.annotate(num_books=Count('book')).filter(num_books__gt=1)
the filter method you are trying to use translates more or less into SQL like this:
SELECT * FROM Link_Book_Course INNER JOIN Book ON (...) WHERE Book.author = ;
So as you see your query has an incomplete where clause.
Anyway, it's not the query you are looking for.
What about something like (assuming author is a simple text field of Book and you want only authors of books referred from Link_Book_Course instances):
Book.objects.filter(pk__in=Link_Book_Course.objects.all().values_list("book", flat=True)).values_list("author", flat=True)
To start with, a filter statement filters on a field matching some pattern. So if Book has a simple ForeignKey to Author, you could have
storeOfAuthorNames = Link_Book_Course.objects.filter(book__author="Stephen King"), but not just
storeOfAuthorNames = Link_Book_Course.objects.filter(book__author).
Once you get past that, I am guessing Book has Author as a ManyToManyField, not a ForeignKey (because a book can have multiple authors, and an author can publish multiple books?) In that case, just filter(book__author="Stephen King") will still not be enough. Try Link_Book_Course.objects.filter(book_author__in=myBookObject.author.all())
I am trying to add an additional custom field to a django model. I have been having quite a hard time figuring out how to do the following, and I will be awarding a 150pt bounty for the first fully correct answer when it becomes available (after it is available -- see as a reference Improving Python/django view code).
I have the following model, with a custom def that returns a video count for each user --
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
positions = models.ManyToManyField('Position', through ='PositionTimestamp', blank=True)
def count(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute(
"""SELECT (
SELECT COUNT(*)
FROM videos_video v
WHERE v.uploaded_by_id = p.id
OR EXISTS (
SELECT NULL
FROM videos_videocredit c
WHERE c.video_id = v.id
AND c.profile_id = p.id
)
) AS Total_credits
FROM userprofile_userprofile p
WHERE p.id = %d"""%(int(self.pk))
)
return int(cursor.fetchone()[0])
I want to be able to order by the count, i.e., UserProfile.objects.order_by('count'). Of course, I can't do that, which is why I'm asking this question.
Previously, I tried adding a custom model Manager, but the problem with that was I also need to be able to filter by various criteria of the UserProfile model: Specifically, I need to be able to do: UserProfile.objects.filter(positions=x).order_by('count'). In addition, I need to stay in the ORM (cannot have a raw sql output) and I do not want to put the filtering logic into the SQL, because there are various filters, and would require several statements.
How exactly would I do this? Thank you.
My reaction is that you're trying to take a bigger bite than you can chew. Break it into bite size pieces by giving yourself more primitives to work with.
You want to create these two pieces separately so you can call on them:
Does this user get credit for this video? return boolean
For how many videos does this user get credit? return int
Then use a combination of #property, model managers, querysets, and methods that make it easiest to express what you need.
For example you might attach the "credit" to the video model taking a user parameter, or the user model taking a video parameter, or a "credit" manager on users which adds a count of videos for which they have credit.
It's not trivial, but shouldn't be too tricky if you work for it.
"couldn't you use something like the "extra" queryset modifier?"
see the docs
I didn't put this in an answer at first because I wasn't sure it would actually work or if it was what you needed - it was more like a nudge in the (hopefully) right direction.
in the docs on that page there is an example
query
Blog.objects.extra(
select={
'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
},
)
resulting sql
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
FROM blog_blog;
Perhaps doing something like that and accessing the user id which you currently have as p.id as appname_userprofile.id
note:
Im just winging it so try to play around a bit.
perhaps use the shell to output the query as sql and see what you are getting.
models:
class Positions(models.Model):
x = models.IntegerField()
class Meta:
db_table = 'xtest_positions'
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
positions = models.ManyToManyField(Positions)
class Meta:
db_table = 'xtest_users'
class Video(models.Model):
usr = models.ForeignKey(UserProfile)
views = models.IntegerField()
class Meta:
db_table = 'xtest_video'
result:
test = UserProfile.objects.annotate(video_views=Sum('video__views')).order_by('video_views')
for t in test:
print t.video_views
doc: https://docs.djangoproject.com/en/dev/topics/db/aggregation/
This is either what you want, or I've completely misunderstood!.. Anywhoo... Hope it helps!
I am looking to find a way to annotate a queryset with the counts of a subset of related items. Below is a subset of my models:
class Person(models.Model):
Name = models.CharField(max_length = 255)
PracticeAttended = models.ManyToManyField('Practice',
through = 'PracticeRecord')
class Club(models.Model):
Name = models.CharField(max_length = 255)
Slug = models.SlugField()
Members = models.ManyToManyField('Person')
class PracticeRecord(PersonRecord):
Person = models.ForeignKey(Person)
Practice = models.ForeignKey(Practice)
class Practice(models.Model):
Club = models.ForeignKey(Club, default = None, null = True)
Date = models.DateField()
I'm looking to make a queryset which annotates the number of club specific practices attended by a person. I can already find the total number of practices by that person with a query of Person.objects.all().annotate(Count('PracticeRecord'))
However I would like someway to annotate the number of practices that a person attends for a specific club.
I would prefer something using the django ORM without having to resort to writing raw SQL.
Thanks.
However I would like someway to annotate the number of practices that a person attends for a specific club.
Let us see.
First, find the specific club.
club = Club.objects.get(**conditions)
Next, filter all Persons who have practiced at this club.
persons = Person.objects.filter(practicerecord__Practice__Club = club)
Now, annotate with the count.
q = persons.annotate(count = Count('practicerecord'))
Edit
I was able to successfully make this work in my test setup: Django 1.2.3, Python 2.6.4, Postgresql 8.4, Ubuntu Karmic.
PS: It is a Good Idea™ to use lower case names for the fields. This makes it far easier to use the double underscore (__) syntax to chain fields. For e.g. in your case Django automatically creates practicerecord for each Person. When you try to access other fields of PracticeRecord through this field you have to remember to use title case.
If you had used lower case names, you could have written:
persons = Person.objects.filter(practicerecord__practice__club = club)
# ^^ ^^
which looks far more uniform.
PPS: It is Count('practicerecord') (note the lower case).
I'm afraid that raw sql is the only option here. Anyway it's not that scary and hard to manage if you put it to model manager.