Dynamic field value in Django class - python

I want to create one dynamic field value for my class in Django using PyCharm.
CATEGORY_CHOICES = (
('on','one'),
('tw','two'),
('th','three'),
('fo','four'),
('fi','five'),
)
class art(models.Model):
Title=models.CharField(max_length=300)
Desciption=models.TextField()
Category=models.CharField(max_length=2, choices=CATEGORY_CHOICES)
I want the category field in my class to take more than one option, maybe two or more.
Any help would be appreciated.

If you want one python model to have multiple categories, then you need django ManyToManyField. Basically one model object could have multiple choices, one choice can also belong to multiple models objects:
class Category(models.Model):
category_name = models.CharField(max_length=10, unique=True)
class Art(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
category = models.ManyToManyField('Category', blank=True)
Note that I put unique=True for category_name to avoid creating duplicate categories.
Something not related, you shouldn't use lower fist in model name, and upper first for field name, that's really BAD naming convention and might confuse others who read your code.
Example:
# create your category in code or admin
one = Category.objects.create(category_name='one')
two = Category.objects.create(category_name='two')
three = Category.objects.create(category_name='three')
# create a new art obj
new_art = Art.objects.create(title='foo', description='bar')
# add category to Art obj
new_art.category.add(one)
new_art.category.add(two)
# category for new art obj
new_art_category = new_art.category.all()
# get only a list of category names
category_names = new_art_category.values_list('category_name', flat=True)
# create another Art obj
new_art2 = Art.objects.create(title="test", description="test")
# assign category to new_art2
new_art2.category.add(two)
new_art2.category.add(three)
Django doc for many to many and python pep8 doc.

Related

Insert multiple keywords in one field

My question is how i can insert multiple keywords in one django field and show them in a template like stackoverflow tags.
Models:
class Jobs(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(blank=True, default='')
company = models.ForeignKey(Company, on_delete=models.CASCADE)
tags = ?????
Create another class and use many-to-many relationship between jobs class (tags) and new class:
class Tags(models.Model):
tag_name=models.CharField()
In jobs class
tags=models.ManyToManyField(Tags)
For show in template you can use for loop, etc.
Make it a Comma separated value.
class Jobs(models.Model):
tags = models.TextField()
def tag_list(self):
return self.tags.split(",")
def add_tag(self, tag_str):
current_tags = self.tag_list()
current_tags.append(tag_str)
current_tags = set(current_tags)
new_tag_string = ",".join(current_tags)
self.tags = new_tag_string
# you could save the model now or let caller save it outside of this method. I suggest letting caller save the model.
def remove_tag(self, tag_str):
current_tags = self.tag_list()
current_tags.remove(tag_str)
new_tag_string = ",".join(current_tags)
self.tags = new_tag_string
# you could save the model now or let caller save it outside of this method. I suggest letting caller save the model.

Updating Many-to-Many relation

I have 3 models (simplified):
class Product(models.Model):
category = models.ForeignKey('Category', related_name='products', to_field='category_name')
brand = models.ForeignKey('Brand', related_name='products', to_field='brand_name')
class Brand(models.Model):
brand_name = models.CharField(max_length=50)
categories = models.ManyToManyField('Category', related_name='categories')
class Category(models.Model):
category_name = models.CharField(max_length=128)
I want to change a Category in admin to a bunch of products, i have a custom admin function written for that. After that I need to update Brand-Categories Many-to-Many relation to check if that Category is still available for a specific Brand. I have written this function:
def brand_refresh():
brands = Brand.objects.all().prefetch_related('shops', 'categories')
products = Product.objects.select_related('shop', 'brand', 'category')
for brand in list(brands):
for category in brand.categories.all():
if not products.filter(category=category).exists():
brand.categories.remove(category)
for product in list(products.filter(brand=brand).distinct('category')):
if product.category not in [None, category]:
brand.categories.add(product.category)
Seems to me this monstro is working, but it takes 2 hours to loop over all cycles (i have ~220k products, 4k+ brands, and ~500 categories). I there any better way to update M2M relation here? I think .prefetch_related() should help here, but what I have now seems have no effect.
Here's a solution for the first part of your loop:
You should try this on a disposable local copy of your database and check that everything works well before running these in production:
from django.db.models import Count
# get a list of all categories which have no products
empty_categories = Category.objects.annotate(product_count=Count('products')).filter(product_count=0).values_list('id', flat=True)
# delete association of empty categories in all brands
Brand.categories.through.objects.filter(category_id__in=list(empty_categories)).delete()
For the second part, perhaps you can do something like this, though I'm not convinced if it's any faster (or even correct as it is):
for brand in Brand.objects.all():
# get a list of categories of all products in the brand
brand_product_categories = brand.products.all().value_list('category__id', flat=True).distinct()
# get the brand's categories
brand_categories = Category.objects.filter(category__brand=brand).value_list('id', flat=True)
# get elements from a not in b
categories_to_add = set(brand_product_categories) - set(brand_categories)
for category_id in categories_to_add:
brand.categories.add(category_id)

How to create custom choice field in Django?

I'm trying to create a form in which user fill in language,description and level of the language (intermediate, advanced...).
The language model:
class Language(models.Model):
shortcut = models.CharField(max_length=6)
name = models.CharField(max_length=50)
first_level_price = models.FloatField() # Basic
second_level_price = models.FloatField() # Intermediate
third_level_price = models.FloatField() # Advanced
def __str__(self):
return self.shortcut+': '+self.name
For each language there exist three levels and prices (first_level_price,second_level_price,third_level_price).
Now, I want user to fill the form. One of the forms should be the level. User just would chose either 'Basic','Intermediate' or 'Advanced'. According to this choice, there would be the price counted.
So if user have chosen 'Basic', there would be price according to first_level_price of the language.
I've tried many ways but no one worked.
level = forms.ChoiceInput(choices=('Basic','Intermediate','Advanced'))
You can update your model like this:
class Language(models.Model):
PRICE_CHOICES = (
('first_level_price', 'Basic'),
('second_level_price', 'Intermediate'),
('third_level_price', 'Advanced'),
)
first_level_price = models.FloatField() # Basic
second_level_price = models.FloatField() # Intermediate
third_level_price = models.FloatField() # Advanced
shortcut = models.CharField(max_length=6)
name = models.CharField(max_length=50)
level = models.CharField(max_length=50, choices=PRICE_CHOICES)
def __str__(self):
return self.shortcut+': '+self.name
Now you can just create a ModelForm using this Model and it should work the way you expect.
When an user selects first_level_price aka Basic membership, you can now grab the price from the first_level_price field.
The choices should a tuple of pairs. The first value in the pair is the data you get into your form instance, the second value of the pair is the displayed value in your template:
level = forms.ChoiceField(choices=(('basic', 'Basic'),
('intermediate', 'Intermediate'),
('advanced', 'Advanced')))
Check django doc details about the choices.

Django.rest_framework: How to serialize one to many to many?

I have some troubles serializing with django.
I have three models, let's say a School, a Room and a Desk (dummy name for example).
Each School have multiple Room, and each Room have multiple Desk.
The classes and their relations look like this :
class School(models.Model):
name = models.CharField()
class Room(models.Model):
name = models.CharField()
school_id = models.ForeignKey(School)
class Desk(models.Model):
row = models.IntegerField()
col = models.IntegerField()
room_id = models.ForeignKey(Room)
I want to be able to serialize a list of School, each directly containing all the desks inside.
The closet I got was by writing in my serialize.py three serializer :
class DeskSerializer(serializers.ModelSerializer):
class Meta:
field = (row, col,)
class RoomSerializer(serializers.ModelSerializer):
desks = DeskSerializer(source='desk_set', many=True)
class Meta:
field = (desks,)
class SchoolSerializer(serializers.ModelSerializer):
rooms = RoomSerializer(source='room_set', many=True)
class Meta:
field = (name, rooms,)
Which return a list of school containing a list of room containing a list of desk, when I want a list of School containing a list of desk
Which source should I use in the School serializer to return directly the desk? Something like source='room_set.desk_set'? Or maybe by using a transform_ function?
PS: the code is write from scratch on the post, please ignore the possible syntax errors
If I'm understanding you correctly, you want the SchoolSerializer to return a nested structure 2 levels deep, but skipping the intermediate model. To do this, I would create a method in your School model to retrieve the Desk instances:
class School(models.Model):
...
def get_desks(self):
rooms = Room.objects.filter(school=self)
desks = Desk.objects.filter(room__in=rooms)
return desks
Then, in your SchoolSerializer include a field that uses that method and renders the returned instances as you wish via your DeskSerializer:
class SchoolSerializer(serializers.ModelSerializer):
...
desks = DeskSerializer(
source='get_desks',
read_only=True
)
class Meta:
field = (name, desks)
The key to understanding how this works is that the model method used as the value for the source argument must simply return instances of that serializer's model. The serializer takes it from there, rendering the object(s) however you defined them within that serializer (in this case the DeskSerializer).
Hope this helps.

Ordering a django model on many-to-may field. Denormalization required?

I have a system for composing items from parts in certain categories
For instance take the following categories:
1: (Location)
2: (Material)
And the following parts:
Wall (FK=1)
Roof (FK=1)
Roof (FK=1)
Brick (FK=2)
Tile (FK=2)
Wood (FK=2)
To compose these items:
Wall.Brick, Roof.Wood, Wall.Wood
class Category(models.Model):
ordering = models.IntegerField()
desc = models.CharField()
class Part:
name = models.CharField()
category = models.ForeignKey(Category)
class Meta:
unique_together = ('name', 'category')
ordering = ['category','name']
class Item:
parts = ManyToManyField(Part)
def __unicode__(self):
return ".".join([p.name for p in self.parts.all()])
Now the question: how do i order the Items? I'd prefer to have them ordered ascending by the composed name, but dont know how.
One way of doing things could be an extra field for the name, that gets updated on the save() method. That would mean denormalizing the model...
If I understand correctly, sort key do not exist in database, so database cannot sort it (or at least on trivially, like using Django ORM).
Under those conditions, yes - denormalize.
It's no shame. As said, normalized dataset is for sissies...

Categories

Resources