Django retrieve key of group ChoiceField - django

retrieve key of group ChoiceField
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
)
class ressource(models.Model):
....
media = models.CharField(max_length=50, choices=MEDIA_CHOICES)
in field media i have vinyl or cd or vhs or dvd...but how retrieve audio,video, unknown ?

You should prove us more details. First of all choices should be iterable object (tuple for example as it's in your case), but your tuple is very complicated in your case (Django can not process this). You should pass something like this as a choices.
choices example (This is good tuple code)
CHOICES = (
('key', 'value') # key will be inserted in database (and validation purposes), and value is just for representation purposes.
)
LANGUAGES = (
('python', 'Python'),
('js', 'JS'),
('ruby', 'Ruby'),
)
Imagine that, you are trying to retrieving object from database.
class ressource(models.Model):
''' This is your model '''
media = models.CharField(max_length=50, choices=MEDIA_CHOICES)
res = ressource.objects.get(pk=1) # retrieve data from database.
print(res.media) # returns string object. you will read actual value of media field
''' But if you want to read all available choices on this field, Django provide as _meta api. You can use it very nice way '''
res = ressource.objects.get(pk=1)
fields = res._meta.fields # This will return tuple-like object.
for field in fields:
if field.name == 'media':
# you want to find "media" field
print(field.choices) # This will return all available choices on your media field
Hope, it helps you. Good luck.

Related

django bulk_create with Null value

models.py
class control(models.Model):
amount = models.IntegerField()
driver = models.ForeignKey(driver, on_delete=models.CASCADE, null=True, blank=True)
views.py
controlValues = [
(1,1,1),
(2,8,None)
]
control.objects.bulk_create([
control(
id = i[0],
amount = i[1],
driver = driver(id = i[2])
) for i in controlValues], ignore_conflicts=True
)
I got error:
bulk_create() prohibited to prevent data loss due to unsaved related object 'driver'.
How can I set Null for driver? I'm using mysql.
If the value is None, you should not construct a driver model with that object, but thus work with:
control.objects.bulk_create([
control(
id=id,
amount=amount,
driver=driver(id=dr_id) if dr_id is not None else None
) for id, amount, dr_id in controlValues],
ignore_conflicts=True
)
or a lot simpler:
control.objects.bulk_create([
control(
id=id,
amount=amount,
driver_id=dr_id
) for id, amount, dr_id in controlValues],
ignore_conflicts=True
)
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from driver to Driver.

Django - How and Where to sort self-referencing model by function

I have a self-referencing model "Location" like this :
class Location(BaseArticle):
name = models.CharField(max_length=200)
parent_location = models.ForeignKey("self",
blank=True,
null=True,
help_text="Fill in if this location is a smaller part of another location.",
on_delete=models.SET_NULL)
description = HTMLField(blank=True,
null=True,
help_text="A description of the location and important features about it")
def __str__(self):
parent_location_string = ""
parent_location = self.parent_location
while parent_location is not None:
parent_location_string = f'{parent_location.name} - {parent_location_string}'
parent_location = parent_location.parent_location
return f'{parent_location_string}{self.name}'
Now when I use Locations with generic Update/Create Views I get a drop-down menu of all locations, since they're ForeignKeys and that's just how generic views and forms handle ForeignKeys.
I would like to have the list of locations in the dropdown alphabetically sorted, but not by the name-field of a location, but by it's __str__() method, e.g. through sorted(location_list, key=lambda x:str(x)).
Time is generally not an issue, I am working with small datasets (<500 entries) that are unlikely to grow beyond 500.
What I tried so far:
Since I'm trying to sort with a function, I can't sort in the model through the Meta classes "ordering" option
Sorting in the Manager by overwriting get_queryset() and using sorted() loses me all the abilities of queryset objects, since sorted() returns a list. I use these functionalities (e.g. exclude/filter) fairly often, so I would like to keep them to avoid a lot of rewriting.
Sorting in the View/Form by overwriting something in the generic UpdateView and CreateView or overwriting something from their used FormClasses (I am not that familiar with manipulating forms) before the template is rendered seems like the best option. The big question here is what needs to be overwritten, as I don't think it's "get_context_data".
Where should I perform this sorting ideally?
If it is in an Update/CreateView or a Form, what do I need to overwrite to do so?
you can build a recursion function to generate expression based on the below codes.
Location.objects.annotate(
order_name=Case(
When(
~Q(parent_location__isnull=True),
then=Case(
When(
~Q(parent_location__parent_location__isnull=True),
then=Concat('parent_location__parent_location__name', Value('-'), 'parent_location__name')
),
default = Concat('parent_location__name', Value('-'), 'name'),
)
),
default = F('name'),
)
)
or try a shot with the recursion function, Noqa
def create_expression(self, fk_field_name, lookup, lookup_field_name, previous_lookup_list=[], depth=1, max_depth=3):
slug = '__'
query_lookup = slug.join([fk_field_name] * depth + [lookup])
next_lookup_list = [slug.join([fk_field_name] * depth + [lookup_field_name]), Value('-')]
if not previous_lookup_list:
previous_lookup_list = [lookup_field_name]
next_lookup_list.extend(previous_lookup_list)
if depth == 1:
default_exp = F(lookup_field_name)
else:
default_exp = Concat(*previous_lookup_list)
if depth >= max_depth:
return (
Case(
When(
~Q(**{query_lookup: True}),
then = Concat(*next_lookup_list)
),
default = default_exp
)
)
return (
Case(
When(
~Q(**{query_lookup: True}),
then= self.create_expression(fk_field_name, lookup, lookup_field_name, next_lookup_list, depth+1)
),
default = default_exp
)
)
case_expression = create_expression('parent_location', 'isnull', 'name')
qset = Location.objects.annotate(order_name = case_expression).order_by('order_name')
or try this:
objs = Location.objects.filter(parent_location__isnull = True).order_by('name')
def get_sub_objs(objs, level=1):
for obj in objs.all():
yield (obj, level)
sub_objs = obj.location_set.all()
if sub_objs:
yield get_sub_objs(sub_objs, level+1)
for obj, level in get_sub_objs(objs):
prefix = '-' * level
print(prefix, obj.name)

DRF filter choices by group name

Let's say I'm making a media library model, and there are these choices in named groups:
models.py
class MediaLibrary(models.Model):
MEDIA_CHOICES = [
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
)
]
media_type = models.CharField(max_length=50, choices=Media_Choices)
If I make a request through DRF, it would return all the objects which match either "Audio" and "Video".
How can I filter the request so that it only returns objects under the "Audio" group. (Or the "Video" group.)
create a model manger and do filter as follows
class MediaManager(models.Manager):
def video_items(self):
return self.get_queryset().filter(media_type='Video')
---your models here--
objects = MediaManager()
then you can call in your views as MediaLibrary.objects.video_items(), similarly filer audio too.

Django Models with Choices as Integer

I am trying to make a feedback app in Django, but I can not make my evaluations.
In my models.py, I have 5 choices from very bad to excellent. But I want them to be usable as numbers so I can evaluate the overall value.
After reading Set Django IntegerField by choices=... name I changed my Ratings from VERYBAD = 'Very bad' to VERYBAD = 1 but no I can't even save my value/form.
Feedback(models.Model):
event = models.ForeignKey(Event)
# Choice
VERYBAD = 'Very bad' #old
BAD = 2 #new
OKAY = 3
GOOD = 4
EXCELLENT = 5
RATING = (
(VERYBAD, 'Very bad'),
(BAD, 'Bad'),
(OKAY, 'Okay'),
(GOOD, 'Good'),
(EXCELLENT, 'Excellent'),
)
# The ratings
organisation = models.CharField(
max_length=9,
choices=RATING,
default=OKAY,
)
....
So in my view, I thought I can do the math but I can not.
def rating(request, event_id):
myevent = Event.objects.get(id=event_id)
feedback_items = Feedback.objects.filter(event=myevent)
num_of_items = len(feedback_items)
def evaluate(feedback_items):
# The overall rating
organisation = 0
for item in feedback_items:
organisation += item.organisation
organisation /= num_of_items
context = {'feedback_items':feedback_items,
'num_of_items': num_of_items,
'myevent': myevent,}
return render(request, 'feedback/rating.html', context)
`
You have to make organisation an IntegerField, obviously - else you can't hope to do integer operations. If you already have this in production, use a set of migrations to add a new IntegerField, populate it from the existing CharField values (using a dict for mapping old string values to new int values), and finally getting rid of the original field.
Also, you may want to learn how to properly use Django's ORM features like getting related models instances and performing aggregations at the DB level, ie:
from django.db.models import Sum, Count, Avg
def rating(request, event_id):
myevent = Event.objects.get(id=event_id)
# this is how you get related models...
feedback_items = myevent.feedback_set.all()
# and this is how you use SQL aggregation functions:
values = feedback_items.aggregate(
sum=Sum('organisation'),
count=Count('pk'),
avg=Avg('organisation')
)
# Now 'values' should be a dict with 'sum', 'count' and 'avg'
# keys, and values['avg'] should be equal to
# float(values['sum']) / values['count']
# FWIW you probably don't need the 'sum' field at all
# I mentionned it so you can check whether 'avg' value
# is correct...
context = {
'feedback_items':feedback_items,
'num_of_items': values['count'],
'avg': values['avg'],
'myevent': myevent,
}
# etc

ModelForm in template with a grouped choice field

I have a grouped category field. The problem is that I've created a search form, but when I try presente the form to the user in the template, it goes wrong.
models.py
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
)
category = models.CharField(max_length=20, choices=MEDIA_CHOICES, verbose_name=_(u'Category'))
forms.py (search)
class SearchingForm(forms.Form):
"Search Box"
search = forms.CharField(max_length=100, required=False, label=(_(u'Search')))
music_kind = forms.MultipleChoiceField(choices=MEDIA_CHOICES, required=False,
label=(_(u'Kind')),
widget=forms.CheckboxSelectMultiple(),
)
template.html
{{ form.search }}
{{ form.place_kind }}
I show the form to the user like this, the problem is when I rendered with a browser I have something like this (in each line, it has a checkbox):
(('vinyl', 'Vinyl'), ('cd', 'CD'))
(('vhs', 'VHS Tape'), ('dvd', 'DVD'))
Unknown
I have delete the 'widget=forms.CheckboxSelectMultiple()' attribute it goes right, but I don't have a checkboxes. So, How I can do it with checkbox fields?
I think you have a data type mismatch here. You're wanting to store multiple values in a single CharField. Sure, you could save a dictionary of key-value pairs in there, but then you'd have to parse it back out into selections, and that's a huge pain.
I would move your MEDIA_CHOICES to a database table, and then in your SearchingForm, you can do a CheckboxSelectMultiple, and the form will behave as expected.
I'm not sure, but I wonder if choice groups are only for select boxes (not checkboxes).