How to order django queryset using the Choices displayed label? - django

Is it possible to order the query set by choices displayed label? If not, can you please advise on how to achieve this? Or is there a way to create a custom ordering?
Thanks in advance!
Choices
C_TYPES = Choices(
('ac_sm', 'ac_sm', _('Ta-Class - Small')),
('ac_md', 'ac_md', _('Ta-Class - Medium')),
('ac_lg', 'ac_lg', _('Ta-Class - Large')),
)
Model
class FactoryLeadType():
c_type = models.CharField(
_('Chiller Type'),
choices=C_TYPES ,
default=C_TYPES .ac_sm,
max_length=50,
)
class Meta:
ordering = ( `get_c_type_display()` )

Related

The best way to do an efficient filter query in django

models.py file
I am not so good at this aspect in Django. Please can someone help me? I wish to know if there is a more efficient way for the class method already_voted
class Vote(TimeStamped):
voter = models.ForeignKey(get_user_model(), verbose_name=_("Vote"), on_delete=models.CASCADE)
contester = models.ForeignKey(Contester, verbose_name=_("Contester"), on_delete=models.CASCADE,
help_text=("The chosen contester"), related_name="votes")
ip_address = models.GenericIPAddressField(
_("Voter's IP"),
protocol="both",
unpack_ipv4=False,
default="None",
unique=True
)
num_vote = models.PositiveIntegerField(_("Vote"), default=0)
class Meta:
unique_together = ('voter','contester')
verbose_name = _("Vote")
verbose_name_plural = _("Votes")
permissions = (
("vote_multiple_times", "can vote multiple times"),
)
....
....
#classmethod
def already_voted(cls, contester_id, voter_id=None, ip_addr=None):
return cls.objects.filter(contester_id=contester_id).exists() and \
(cls.objects.filter(ip_address=ip_addr).exists() or \
cls.objects.filter(voter_id=voter_id).exists())
The class method may be right, but your model needs one more index:
contester = models.ForeignKey( db_index= True #... )
Notice that:
voter doesn't need index because is on first place on unique_together constraint.
contester needs index because, despite it is on unique_together, doesn't is place on first position of the constraint.
ip_address doesn't need index because has unique constraint.
Also:
unique_together is deprecated and should be a list of tuples (not just a tuple)
Edited
Edited 5 feb 2021 due to OP comment
You can get results in just one hit using Exists but it is less readable, also, I'm not sure if it is more efficient or the best way:
from django.db.models import Exists
q_ip=Vote.objects.filter(ip_address="1")
q_voter=Vote.objects.filter(voter=2)
already_voted=(
Vote
.objects
.filter(contester=3)
.filter(Exists(q_ip)|Exists(q_voter))
.exists())
The underlying sql, you can see this is just one query:
SELECT ( 1 ) AS "a"
FROM "a1_vote"
WHERE ( "a1_vote"."contester" = 3
AND ( EXISTS(SELECT U0."id",
U0."voter",
U0."contester",
U0."ip_address",
U0."num_vote"
FROM "a1_vote" U0
WHERE U0."ip_address" = '1')
OR EXISTS(SELECT U0."id",
U0."voter",
U0."contester",
U0."ip_address",
U0."num_vote"
FROM "a1_vote" U0
WHERE U0."voter" = 2) ) )
LIMIT 1

Filtering X most recent entries in each category of queryset

Question is regarding filtering X most recent entries in each category of queryset.
Goal is like this:
I have a incoming queryset based on the following model.
class UserStatusChoices(models.TextChoices):
CREATOR = 'CREATOR'
SLAVE = 'SLAVE'
MASTER = 'MASTER'
FRIEND = 'FRIEND'
ADMIN = 'ADMIN'
LEGACY = 'LEGACY'
class OperationTypeChoices(models.TextChoices):
CREATE = 'CREATE'
UPDATE = 'UPDATE'
DELETE = 'DELETE'
class EntriesChangeLog(models.Model):
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
)
object_id = models.PositiveIntegerField(
)
content_object = GenericForeignKey(
'content_type',
'object_id',
)
user = models.ForeignKey(
get_user_model(),
verbose_name='user',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='access_logs',
)
access_time = models.DateTimeField(
verbose_name='access_time',
auto_now_add=True,
)
as_who = models.CharField(
verbose_name='Status of the accessed user.',
choices=UserStatusChoices.choices,
max_length=7,
)
operation_type = models.CharField(
verbose_name='Type of the access operation.',
choices=OperationTypeChoices.choices,
max_length=6,
)
And I need to filter this incoming queryset in a such way to keep only 4 most recent objects (defined by access_time field) in each category. Categories are defined by ‘content_type_id’ field and there are 3 possible options.
Lets call it ‘option1’, ‘option2’ and ‘option3’
This incoming queryset might contain different amount of objects of 1,2 or all 3 categories. This is can’t be predicted beforehand.
DISTINCT is not possible to use as after filtering operation this queryset might be ordered.
I managed to get 1 most recent object in a following way:
# get one most recent operation in each category
last_operation_time = Subquery(
EntriesChangeLog.objects.filter(user=OuterRef('user')).values('content_type_id').
annotate(last_access_time=Max(‘access_time’)).values_list('last_access_time', flat=True)
)
queryset.filter(access_time__in=last_operation_time)
But I have a hard time to figure out how to get last 4 most recent objects instead of last one.
This is needed for Django-Filter and need to be done in one query.
DB-Postgres 12
Do you have any ideas how to do such filtration?
Thanks...
pk_to_rank = queryset.annotate(rank=Window(
expression=DenseRank(),
partition_by=('content_type_id',),
order_by=F('access_time').desc(),
)).values_list('pk', 'rank', named=True)
pks_list = sorted(log.pk for log in pk_to_rank if log.rank <= value)
return queryset.filter(pk__in=pks_list)
Managed to do it only this way by spliting queryset in 2 parts. Option with 3 unions is also possible but what if we have 800 options instead 3 - make 800 unions()??? ges not...

Django choicefield choices not displaying

Im using a choicefield and setting 2 values - 'students', 'teachers',
but for some reason when the form displays it only shows 'teachers' and not 'students'.
class SignUpShortForm(SignUpForm):
role = forms.ChoiceField(
choices=[],
widget=forms.Select(attrs={'class':'form-control'}),
label='I am a...',
)
self.fields['role'].choices = [('Teacher', 'Teacher2')]
Please look here You add to your choices only values without keys. Code might look like this:
CHOICES = (
('students', 'Students'),
('teachers', 'Teachers'),
)
class SignUpShortForm(SignUpForm):
role = forms.ChoiceField(
choices=CHOICES,
widget=forms.Select(attrs={'class':'form-control'}),
label='I am a...',
)

How can I apply forms.ModelChoiceField to a model-form? (Django)

I am a complete beginner with little understanding of widgets etc, I'm using a model-based form that needs to have a drop-down selection box for dietary preferences. this is what I tried to do earlier (obviously wrong):
class Register(models.Model):
dietry_preferences = forms.ModelChoiceField( queryset = "none", "vegetarian", "vegan", "halaal", "kosher", empty_label = "none")
can anyone shed some light on this?
ModelChoiceField is related to forms. It is unclear if you want to build a form or a model. Therefore, for a model:
class Register(models.Model):
PREFERENCES = (
('vt', "vegetarian"),
('vg', "vegan"),
....
)
dietry_preferences = models.CharField(max_length=5, choices=PREFERENCES)
Then you would build a modelform appropriately.
Reference: Django documentation, choices
Otherwise, for a form:
class Register(forms.Form):
PREFERENCES = (
('vt', "vegetarian"),
('vg', "vegan"),
....
)
dietry_preferences = forms.ChoiceField(choices=PREFERENCES)
Reference: Django documentation, ChoiceField

Django - Use Radio Buttons or Drop Down List for input

I made a model something like this:
class Enduser(models.Model):
user_type = models.CharField(max_length = 10)
Now I want user_type to have only one of the given values, say any one from ['master', 'experienced', 'noob']
Can I do this with Django?
Also, how can I display a list of radio buttons or drop-down list/select menu to chose one of these values?
You can take advantage of the choices attribute for CharField:
class Enduser(models.Model):
CHOICES = (
(u'1',u'master'),
(u'2',u'experienced'),
(u'3',u'noob'),
)
user_type = models.CharField(max_length = 2, choices=CHOICES)
This will save values 1,2 or 3 in the db and when retrieved the object, it will map it to master, experienced or noob. Take a look at the docs for more info.
Hope this helps!
Use model field choices:
CHOICES = (
('foo', 'Do bar?'),
...
)
class Enduser(models.Model):
user_type = models.CharField(max_length = 10, choices=CHOICES)