get long name during annotate in django ORM query - django

I have this model:
class Rank(models.Model):
RANK_TYPE_CHOICES = (
('O', 'Officer'),
('E', 'Enlisted'),
('V', 'Civilian'),
('C', 'Cadet'),
)
ShortName = models.CharField(max_length=50)
LongName = models.CharField(max_length=500)
Type = models.CharField(max_length=1, choices=RANK_TYPE_CHOICES, default='O')
Genre = models.ForeignKey(Genre, on_delete=models.DO_NOTHING)
Career = models.ForeignKey(Career, on_delete=models.DO_NOTHING)
image = models.ForeignKey(Photos, on_delete=models.DO_NOTHING)
when I perform this ORM action:
models.Rank.objects.values('Type').annotate(total=Count('Type')).order_by()
I get this response
<QuerySet [{'Type': 'O', 'total': 1}]>
Exactly as I want.
However, as you can see, it gives me the short type. How do I make it show the long name instead of the type choice short name?
Thanks.

You will need to add the values manually.
rank_dict = dict(RANK_TYPE_CHOICES)
for obj in my_ranks:
obj['full_type'] = rank_dict[obj['Type']]

Related

How to get the value being referenced in django?

Here I need to get the student_id in the values. I have defined s_id but if values() is called it does not show.
FeeSection.objects.get(id=1).section.sectionstudent_set.annotate(s_id=F('student__id')).get(student__id=1).qouta.feeqouta_set.all().values()
This query returns:
<QuerySet [{'id': 1, 'qouta_id': 2, 'type_id': 1, 'amount': Decimal('200.00')}, {'id': 2, 'qouta_id': 2, 'type_id': 2, 'amount': Decimal('10.00')}]>
what I need is: 's_id':1
<QuerySet [{'id': 1,'s_id':1, 'qouta_id': 2, 'type_id': 1, 'amount': Decimal('200.00')}, {'id': 2, 's_id':1,'qouta_id': 2, 'type_id': 2, 'amount': Decimal('10.00')}]>
Models:
class SectionStudent(models.Model):
student = models.ForeignKey('users.Student', on_delete=models.CASCADE)
section = models.ForeignKey(Section, on_delete=models.CASCADE, )
roll = models.IntegerField()
qouta = models.ForeignKey(Qouta, on_delete=models.CASCADE)
class FeeQouta(models.Model):
qouta = models.ForeignKey(to='section.qouta', on_delete=models.CASCADE)
type = models.ForeignKey(FeeType, on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=10, decimal_places=2)
class FeeSection(models.Model):
section = models.ForeignKey('section.Section', on_delete=models.CASCADE)
month = models.IntegerField(choices=MONTH_CHOICES, default=datetime.datetime.now().month)
year = models.IntegerField(choices=YEAR_CHOICES, default=datetime.datetime.now().year)
class Section(models.Model):
section = models.CharField(max_length=1, verbose_name='section')
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
shift = models.ForeignKey(Shift, on_delete=models.DO_NOTHING)
cls = models.ForeignKey(Class, on_delete=models.CASCADE, verbose_name='Class')
version = models.ForeignKey(Version, on_delete=models.DO_NOTHING)
year = models.IntegerField(choices=YEAR_CHOICES, default=datetime.datetime.now().year)
class Student(models.Model):
sid = models.CharField(verbose_name='Student ID', max_length=10, unique=True)
class Qouta(models.Model):
name = models.CharField(max_length=20)
version = models.ForeignKey(Version, on_delete=models.PROTECT)
cls = models.ForeignKey(Class, on_delete=models.PROTECT, verbose_name='Class')
get() returns a model instance, so anything following after it is basically a new SQL query, no matter how much are you chaining it together. Your code does this right now:
fee_section = FeeSection.objects.get(id=1)
# this starts new SQL query from zero
section_student = fee_section.section.sectionstudent_set.annotate(s_id=F('student__id')).get(student__id=1)
# this starts new SQL query from zero, `annotate()` from above get lost
fee_quotas = fee_section.qouta.feeqouta_set.all().values()
If you want to keep chaining things, you have to replace your get() with filter(), however the resulting SQL query seems a bit wild...

how to limit_choices_to self field id in django model

how use limit_choices_to in django
class Category(models.Model):
"""
商品类别
"""
CATEGORY_TYPE = (
(1, "一级类目"),
(2, "二级类目"),
(3, "三级类目"),
)
def limit_category_type_choice(self):
obj = Category.objects.get(category_type=self.category_type)
field_object = Category._meta.get_field('category_type')
field_value = field_object.value_from_object(obj)
return {'category_type__lte': field_value}
category_id = models.AutoField(primary_key=True, verbose_name='category_id')
category_title = models.CharField(default='', max_length=50, verbose_name='目录标题', help_text='目录标题')
category_name = models.ForeignKey(LinkDesignate,blank=True, null=True, to_field='link_des_text_id', related_name='category', on_delete=models.CASCADE)
category_type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name="类目级别", help_text="类目级别")
parent_category = models.ForeignKey("self", limit_choices_to=self.limit_category_type_choice, null=True, blank=True, verbose_name="父类目级别", help_text="父目录",
related_name="sub_cat", on_delete=models.CASCADE)
class Meta:
verbose_name = "产品目录"
verbose_name_plural = verbose_name
i know this
limit_choices_to=self.limit_category_type_choice,
this is wrong , because name 'self' is not defined
how can to use the function limit_category_type_choice
the document is:
def limit_pub_date_choices():
return {'pub_date__lte': datetime.date.utcnow()}
limit_choices_to = limit_pub_date_choices
how can i change my Function limit_category_type_choice without self
but can use the self instance
Put the def out of the class ;-)
This was my solution, it is similar.
It limits the films which can be choosen for an event.
Films with status '2' in class Film over ForeignKey OR
over reverse ForeignKey: Films with an Event with date in the future
the model:
def limit_film_choices():
date = str(datetime.datetime.now())
result = Q( status = '2') | Q( event_film__date__gte = date)
return result
class Film(models.Model):
STATUS_COICES = [
('1' , 'a'),
('2' , 'b'),
]
status = models.CharField( max_length=16, choices=STATUS_COICES, default='1')
class Event(models.Model):
film = models.ForeignKey('filme.Film', on_delete=models.CASCADE, related_query_name='event_film',
related_name='event_film', blank = True, null=True, limit_choices_to = limit_film_choices)
date = models.DateTimeField(auto_now=False, null=True)

Django link multiple table

I have four models as follows:
class deals_view(models.Model):
deal = models.ForeignKey(dealsModel, on_delete=models.CASCADE, related_name='deal_view')
client = models.ForeignKey(client_profileModel, on_delete=models.CASCADE)
client_ip = models.GenericIPAddressField(protocol='both')
date_added = models.DateField(auto_now_add=True)
class client_profile(models.Model):
GENDER_NAME = (
('M', 'Male'),
('F', 'Female'),
('O', 'Others')
)
user = models.ForeignKey(User, unique=True, on_delete=models.CASCADE)
gender = models.CharField(max_length=1, choices=GENDER_NAME, null=True)
address = models.CharField(max_length=100, null=True)
dob = models.DateField(null=True)
class deals(models.Model):
GENDER_NAME = (
('M', 'Male'),
('F', 'Female'),
('O', 'Others'),
('A', 'All'),
)
AGE_RANGE = (
('A1', '18-25'),
('A2', '25-40'),
('A3', '40-55'),
('A4', '55-100'),
('A5', '18-100'),
('AL', '13-100'),
('T1', '13-18')
)
store = models.ForeignKey(storesModel, on_delete=models.CASCADE, related_name='deals_store')
title = models.CharField(max_length=30)
description = models.CharField(max_length=160)
price = models.DecimalField(max_digits=6, decimal_places=2)
category = models.ForeignKey(categoriesModel, on_delete=models.PROTECT)
targeted_gender = models.CharField(max_length=1, choices=GENDER_NAME)
targeted_age = models.CharField(max_length=2, choices=AGE_RANGE)
is_active = models.BooleanField(default=False)
created_date = models.DateField(auto_now_add=True)
expiry_date = models.DateField(default=(dt.now() + td(days=30)))
class stores(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, unique=True)
about = models.CharField(max_length=300)
company_name = models.CharField(max_length=100)
I want to list all deals for the current month and its corresponding number of views per gender.
so far I've tried the following; i can get all the info but it seems i have multiple hits of the database:
self.storeObj = storesModel.objects.prefetch_related('deals_store').get(id=storeid)
self.deals_store = self.storeObj.deals_store
segmentedInfo = self.deals_store.all().prefetch_related('deal_view').filter(deal_view__date_added__range=((datetime.today() - timedelta(days=30)), datetime.now())).distinct()
for seg in segmentedInfo:
for x in seg.deal_view.select_related('client').distinct():
print(x.client.gender)
Is there any way i can optimise my queries to get the number of views per gender for a deal of a specific store?
Yes, we can do some filtering in the related client_profile model, and then use a .annotate(..) to count the number of distinct ids.
from django.db.models import Count
client_profile.objects.filter(
deals_view__deal__store_id=storeid,
deals_view__date_added__range=(datetime.today()-timedelta(days=30), datetime.now())
).values('gender').annotate(
n=Count('id', distinct=True)
).order_by('gender')
This will result in a QuerySet that contains dictionaries with the gender, and the number of client_profiles, like:
<QuerySet [
{'gender': 'M', 'n': 1425},
{'gender': 'M', 'n': 1302},
{'gender': 'O', 'n': 173}
]>
If there are no client_profiles with a given gender that have seen the deal, then it is not part of the QuerySet.

What to define model for forms.ChoiceField in Django?

I have made a form that contains a field like:
sex = forms.ChoiceField(choices= SEX)
Where:
SEX = (
('F','Female'),
('M','Male'),
('U','Unsure'),
)
Now I'm wondering how best the sex field should be defined the model? I know that it can be done like:
class UserProfile(models.Model):
user = models.ForeignKey('User')
sex = models.CharField(max_length=10)
But isn't there a better option than CharField?
You've set your choices up as strings so it should be a CharField(max_length=1, choices=SEX) in the model. You could then use a ModelForm instead of repeating all the logic in a separate form. For example:
# models.py
class MyModel(models.Model):
SEX_CHOICES = (
('F', 'Female',),
('M', 'Male',),
('U', 'Unsure',),
)
sex = models.CharField(
max_length=1,
choices=SEX_CHOICES,
)
# forms.py
class MyForm(forms.MyForm):
class Meta:
model = MyModel
fields = ['sex',]
Since Django 4.04
Now there is a more proper way to do it. Check it out.
Basically, you'll need to create a enum class like
class SexOptions(models.TextChoices):
FEMALE = 'F', 'Female'
MALE = 'M', 'Male'
UNSURE = 'U', 'Unsure'
class UserProfile(models.Model):
user = models.ForeignKey('User')
sex = models.CharField(max_length=1, choices=SexOptions.choices)
Old Answer
class UserProfile(models.Model):
SEX_FEMALE = 'F'
SEX_MALE = 'M'
SEX_UNSURE = 'U'
SEX_OPTIONS = (
(SEX_FEMALE, 'Female'),
(SEX_MALE, 'Male'),
(SEX_UNSURE, 'Unsure')
)
user = models.ForeignKey('User')
sex = models.CharField(max_length=1, choices=SEX_OPTIONS)
I do prefer this way, it's easier to reference the options inside your code.
UserProfile.objects.filter(sex__exact=UserProfile.SEX_UNSURE)

Display forms choice in template-Django

On the template, when I call person.health_issue, I am getting '1','2' instead of 'Abdominal pain','Anaphylaxis'. How to display the value ('Abdominal pain','Anaphylaxis') instead of the code(1 or2 etc).
I tried with this also {{ person.get_health_issue_display }} in template,it is not displayed anything.
forms.py
HEALTH_USSUES = (
('1', 'Abdominal pain'), ('2', 'Anaphylaxis'), ('3', 'Asthma'),
('4', 'Bruising'), ('5', 'Chest pains'), ('6', 'Coughs or Colds')
)
class PersonActionsForm(forms.ModelForm):
action = forms.MultipleChoiceField(widget=forms.Select(), choices=HEALTH_USSUES, required=False)
models.py
class ReportPerson(models.Model):
report = models.ForeignKey(Report)
name = models.CharField('Name', max_length=100)
first_aid = models.BooleanField('First aid', default=False)
health_issue = models.IntegerField(default=0)
views.py
def report_template(request):
""""""
person = ReportPerson.objects.get(pk=person_id)
""""""
return render(request, 'event/print.html',
{
'person':person
})
can any one tell me how to do this.
Thanks
As you don't have any choices set in model field health_issue you need to write the get_health_issue_display method by your self i will name it as health_issue_display so that default get_FOO_display method not gets overridden:
HEALTH_USSUES = (
(1, 'Abdominal pain'), (2, 'Anaphylaxis'), (3, 'Asthma'),
(4, 'Bruising'), (5, 'Chest pains'), (6, 'Coughs or Colds')
)
class ReportPerson(models.Model):
report = models.ForeignKey(Report)
name = models.CharField('Name', max_length=100)
first_aid = models.BooleanField('First aid', default=False)
health_issue = models.IntegerField(default=1)
def health_issue_display(self):
for c in HEALTH_USSUES:
if c[0] == self.health_issue:
return c[1]
Or just add choices in the model field:
health_issue = models.IntegerField(default=1, choices=HEALTH_USSUES)
Now you have get_health_issue_display.
Also make the first value in every choice as integer (1, 'Abdominal pain') rather than string '1'. Just to remove the confusion.
You have default=0 which does not exists in choices. Change it to default=1