which is best ForeignKey or choices ? what is the different? - django

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

Related

Bin a queryset using Django?

Let's say we have the following simplistic models:
class Category(models.Model):
name = models.CharField(max_length=264)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "categories"
class Status(models.Model):
name = models.CharField(max_length=264)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "status"
class Product(models.Model):
title = models.CharField(max_length=264)
description = models.CharField(max_length=264)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10)
status = models.ForeignKey(Status, on_delete=models.CASCADE)
My aim is to get some statistics, like total products, total sales, average sales etc, based on which price bin each product belongs to.
So, the price bins could be something like 0-100, 100-500, 500-1000, etc.
I know how to use pandas to do something like that:
Binning column with python pandas
I am searching for a way to do this with the Django ORM.
One of my thoughts is to convert the queryset into a list and apply a function to get the apropriate price bin and then do the statistics.
Another thought which I am not sure how to impliment, is the same as the one above but just apply the bin function to the field in the queryset I am interested in.
There are three pathways I can see.
First is composing the SQL you want to use directly and putting it to your database with a modification of your models manager class. .objects.raw("[sql goes here]"). This answer shows how to define group with a simple function on the content - something like that could work?
SELECT FLOOR(grade/5.00)*5 As Grade,
COUNT(*) AS [Grade Count]
FROM TableName
GROUP BY FLOOR(Grade/5.00)*5
ORDER BY 1
Second is that there is no reason you can't move the queryset (with .values() or .values_list()) into a pandas dataframe or similar and then bin it, as you mentioned. There is probably a bit of an efficiency loss in terms of getting the queryset into a dataframe and then processing it, but I am not sure that it would certainly or always be bad. If its easier to compose and maintain, that might be fine.
The third way I would try (which I think is what you really want) is chaining .annotate() to label points with the bin they belong in, and the aggregate count function to count how many are in each bin. This is more advanced ORM work than I've done, but I think you'd start looking at something like the docs section on conditional aggregation. I've adapted this slightly to create the 'price_class' column first, with annotate.
Product.objects.annotate(price_class=floor(F('price')/100).aggregate(
class_zero=Count('pk', filter=Q(price_class=0)),
class_one=Count('pk', filter=Q(price_class=1)),
class_two=Count('pk', filter=Q(price_class=2)), # etc etc
)
I'm not sure if that 'floor' is going to work, and you may need 'expression wrapper' to ensure the push price_class into the write type of output_field. All the best.

Using property in Django

I need help with the following situation.
My app has the following models:
class Person(models.Model):
person_sequential_nr = models.IntegerField(primary_key=true)
person_id = models.CharField(max_length=10)
person_name = models.CharField(max_length=200)
year = models.CharField(max_length=4)
#property
def _summarize_goods(self):
return self.goods_set.all().aggregate(Sum('good_value')).values()
patrimony = property(_summarize_goods)
class Goods:
person_sequential_nr = models.Foreignkey()
good_description = models.CharField(max_length=200)
good_value = models.DecimalField(max_digits=12, decimal_places=2)
year = models.CharField(max_length=4)
Year is a string like 2012, 2010, 2008, etc
person_sequential_nr is specific (different) for each year.
person_id is the same for all years.
The intention of _summarize_goods is to totalize all goods of a person in a specific year.
1) How can I get the top ten people with the highest patrimonies?
When I call Person.patrimony it says "TypeError: 'property' object is not callable"
Person._summarize_goods works, but I have no idea how to order it.
2) How can I calculate the patrimony variation from of a person from one year to another (in the past)?
I would like to have something like: variation = (patrimony(year='2012')/patrimony(year='2010') - 1) * 100
I suppose that variation should be also a property, because I would like to use it to order some records.
An additional problem is the the person data may exist in 2012, but may not exist in a year in the past (e.g. 2010). So I need to handle this situation.
3) How can I create a view to show the patrimony of a person?
self.goods_set.all().aggregate(Sum('good_value')) was returning a dictionary, so I added .values() to extract only the values of it, then I got a list.
But wen I use str(Person._summarize_goods) it seemns that I still have a list.
Because when I call:
all_people = Person.objects.all()
people_list = [[p.person_name, str(p._summarize_goods)] for p in all_people]
output = ','.join(people_list)
return HttpResponse(output)
It shows an error referring the line output =
TypeError at /
sequence item 0: expected string, list found
EDITING...
Find some answers:
After removing the decoration (thanks Daniel) Person.patrimony is working so:
1) How can I get the top ten people with the highest patrimonies?
This was solved with the code below:
def ten_highest_pat():
people_2012 = Person.objects.all().filter(year=2012)
return sorted(people_2012, key=lambda person: person.patrimony, reverse=True)[:10]
2) How can I calculate the patrimony variation from of a person from one year to another (in the past)?
I tried the code below, which works, but is too slow. So I would thank you if someone has a sugestion how can I improve it.
def _calc_pat_variation(self):
c = Person.objects.all().filter(person_id = self.person_id, year__lt=self.year).order_by('-year')
if c.count() >= 1 and c[0].patrimony != 0:
return ((self.patrimony / c[0].patrimony) - 1) * 100
else:
return 'Not available'
pat_variation = property(_calc_pat_variation)

django order_with_respect_to with related objects

I am working on a django application that contains a bunch of related objects. I have test objects, each with an ordered set of questions (each question has a correct_answer property). And I also have test attempt objects, which are related to test objects by a foreign key and each have their ordered set of question attempts each with a choice property. Essentially, each question attempt corresponds to a question (a test attempt only passes validation of it has the same number of question attempts as the test it is related to has questions), and then I can check the percentage right and wrong by outputting a values_list of correct_answers and choices and comparing the two lists. My model looks something like this:
ANSWERS = ((0,''),(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E'))
class Question(models.Model):
test = models.ForeignKey(Test,related_name='questions')
correct_answer = models.IntegerField(max_length=1,choices=ANSWERS)
def _get_score(self):
answers = self.test.test_attempts.values_list('answers_choice',flat=True)[self.test.get_question_order().index(self.pk)::self.test.questions.count()]
scores = [x==self.correct_answer for x in answers]
length = len(correct)
if length == 0:
return 0
return float(sum(scores))/length
score = property(_get_score)
class Meta:
order_with_respect_to = 'test'
class Test(models.Model):
testID = models.CharField(max_length=50,verbose_name='Test identification information')
def _get_score(self):
scores = [x.score for x in self.test_attempts.all()]
length = len(scores)
if length == 0:
return 0
return float(sum(scores))/length
score = property(_get_score)
class QuestionAttempt(models.Model):
test_attempt = models.ForeignKey(TestAttempt,related_name='answers')
choice = models.IntegerField(max_length=1,choices=ANSWERS)
def isCorrect(self):
return self.choice == Question.objects.get(pk=self.test_attempt.test.get_question_order()[self.test_attempt.get_questionattempt_order().index(self.pk)]).correct_answer
class Meta:
order_with_respect_to = 'test_attempt'
class TestAttempt(models.Model):
test = models.ForeignKey(Test,related_name='test_attempts')
student = models.ForeignKey(UserProfile,related_name='test_attempts')
def _get_score(self):
responses = self.answers.values_list('choice',flat=True)
correctAnswers = self.test.questions.values_list('correct_answer',flat=True)
s = [x==y for x,y in zip(responses,correctAnswers)]
length = len(s)
if length == 0:
return 0
return float(sum(s))/length
score = property(_get_score)
class Meta:
unique_together = ('student','test')
If you take a look at the QuestionAttempt Model and within that, the isCorrect method, you see how my predicament. It seems as if that is the only way to check obtain a per-question granularity to check if a given question attempt is right or wrong. My problem is that this one statement is literally 3 or 4 unique database queries (I can't even tell there are so many). I was thinking of using F statements, but I don't know the default name of the ordering column that django uses when specifying order_with_respect_to. Also, to get the score for a given question, I need to do 3+ db queries as well. Is there a better way to do this? How can I access the db entry for the order. I did some searching and theres a property called _order applied to both the question and the questionattempt model. Can I reliably use this? This would greatly simplify some of the code because I could query the database using a filter on the _order property as well
The problem is with your logic in setting up the models. A QuestionAttempt should be directly related to a Question. Relying on the ordering to be the same is dubious at best, and most likely will fail at some point.

Django - Choices for Models

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.

Finding object count where a field is unique in Django

I have a model that is something like this:
class Input(models.Model):
details = models.CharField(max_length=1000)
user = models.ForeignKey(User)
class Case(Input):
title = models.CharField(max_length=200)
views = models.IntegerField()
class Argument(Input):
case = models.ForeignKey(Case)
side = models.BooleanField()
A user can submit many arguments, per case. I want to be able to say how many users have submitted side=true arguments.
I mean if 1 user had 10 arguments and another user had 2 arguments (both side=true)
I'd want the count to be 2, not 12.
Update:
I am using these methods on the Case object:
def users_agree(self):
return self.argument_set.filter(side=True).values('user').distinct()
def users_disagree(self):
return self.argument_set.filter(side=False).values('user').distinct()
My template code calls count() on them.
Can you try:
Argument.objects.filter(side=True).values('case__user').distinct().count()
I think it does what you want. It issues one SQL query:
SELECT COUNT(DISTINCT "example_input"."user_id") FROM "example_argument" INNER JOIN "example_case" ON ("example_argument"."case_id" = "example_case"."input_ptr_id") INNER JOIN "example_input" ON ("example_case"."input_ptr_id" = "example_input"."id") WHERE "example_argument"."side" = True
Edit:
For this_case, get all users whose argument.side is True:
Argument.objects.filter(case__id=this_case.id, side=True).values('user').distinct()