Django - model with several tag fields - python

I need a model with several "tag" fields. For example:
class Food(models.Model):
type = SomeTagField()
manufacturer = SomeTagField()
It could be done using many-to-many relationships, but I'm not sure if this is a correct approach. It looks a little bit overcomplicated.
I've tried django-tagit and its TaggableManager but without luck:
ValueError: You can't have two TaggableManagers with the same through model.
What would be a correct way to handle that?

"Over-complicated" is subjective, but I would use ManyToManyField
class FoodType(models.Model):
country = SomeField()
nutrients = SomeField()
...
class Manufacturer(models.Model):
name = SomeField()
cost = SomeField()
...
class Food(models.Model):
type = models.ManyToManyField(FoodType, ...)
manufacturer = models.ManyToManyField(Manufacturer, ...)

Related

How can I access attributes of a model in admin.TabularInline that is at the end of a manytomany relationship with defined through model

I am facing the following scenario;
#models.py
class A(models.Model):
dog = models.CharField(...)
cat_values = models.ManyToManyField(B, through='AtoB')
class AtoB(models.Model):
one = models.ForeignKey(A)
two = models.ForeignKey(B)
class B(models.Model):
cat = models.CharField(...)
#admin.py
class BForm(forms.ModelForm):
cat = forms.TextInput()
class Meta:
model = A.cat_values.through
exclude = []
class BInline(admin.TabularInline):
model = B.cat_values.through
form = BForm
fields = ['cat', ]
readonly_fields = ['cat', ]
#admin.register(A)
class AAdmin(admin.ModelAdmin):
inlines = [BInline,]
When I try to run server, I get the following error message;
<class 'agregator.admin.AAdmin.BInline'>: (admin.E035) The value of 'readonly_fields[0]' is not a callable, an attribute of 'BInline', or an attribute of 'agregator.AtoB'.
No other forms of access really work like A.cat_values or A.cat_values.two
I partially understand where is the problem coming from. It does not let me access the B's attributes, only AtoB's attributes. I have tried to work around it but unsuccessfully. Have read the documentation but there is nothing about accessing the attributes in this case scenario, only without a defined through model.
eg https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/ or https://docs.djangoproject.com/en/3.1/topics/db/models/
I need to display the cat attribute in the inline in the A's admin. Any help would be much appreciated.
So I have solved the issue. The key was to define properties for the AtoB model.
For example;
class AtoB(models.Model):
one = models.ForeignKey(A)
two = models.ForeignKey(B)
#property
def cat(self):
return self.two.cat

how to make django's choice option dynamic in the model

I am creating a reference lexicon in django. The lexicon is a translator for certain fields from one language to another.
The lexicon goes like this: 'id','lng1','lng2','lng3'
I have a model that uses the lexicon in one of the fields.
class lexicon(models.Model):
lng1 = model.Charfield(max_length=255)
lng2 = model.Charfield(max_length=255)
lng3 = model.Charfield(max_length=255)
lexicon_choices = (
(lng1=lng1),
(lng2=lng2),
(lng3=lng3)
)
class model(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = model.Charfield(max_length=3,CHOICES=lexicon_choices)
This works ok, but I want to know, is there a way to glean out the column names from class lexicon and port them as the choices for class model? I am using Django Rest Framework for the first time, and I want to make sure the right lng is there.
Thanks
You can use a ForeignKey on the lexicon class to your main model. This will ensure a One to Many relationship. And you can store the values in database.
yes, possible. but in admin.py
class CustomForm(forms.ModelForm):
class Meta:
model = YourModel
def __init__(self, *args, **kwargs):
super(CustomForm, self).__init__(*args, **kwargs)
choices = [(x.id, x.name) for x in Lexicon.objects.all()]
self.fields['field1'].choices = choices
class YourModelAdmin(admin.ModelAdmin):
form = CustomForm
and your original model looks like:
class YourModel(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = models.CharField(max_length=10, choices=(('--', '--'),))
IMPORTANT: do not name your class names with lower case. ;)

Ordering case insensitive in django-tables2

First post in this awesome community that I have been reading a long time ago :)
I've encountered with a problem when using this fantastic library "django-tables2". When I'm ordering a column by a CharField, it does a case sensitive ordering resulting unexpected behaviour like this:
Iago
Pablo
iago
I want to be ordered in a more natural way:
Iago
iago
Pablo
This is my simplified code for the table:
class Inquiry(models.Model):
...
contact_last_name = models.CharField(max_length=50)
...
class Hometable(tables.Table):
contact_last_name = tables.Column(verbose_name="Contact", order_by=('contact_last_name'))
class Meta:
model = Inquiry
fields= ('contact_last_name',)
I know in Django 1.8 there is a built-in function Lower to make a insensitive order_by, but it doesn't work with django tables:
contact_last_name = tables.Column(verbose_name="Contact", order_by=(Lower('contact_last_name')))
It results in an exception:
TypeError at /
'Lower' object is not iterable
Has anyone done anything similar with django-tables2?
Thank you!
UPDATE: The solution is to make a annotation in the view with the lowercase fields, an then will be available to order by in the table.
class Inquiry(models.Model):
...
contact_last_name = models.CharField(max_length=50)
...
class Hometable(tables.Table):
contact_last_name = tables.Column(verbose_name="Contact", order_by=('contact_last_name_lower'))
class Meta:
model = Inquiry
fields= ('contact_last_name',)
And make the proper annotation in the queryset when configuring the table as Alasdair purposed:
inquiries = inquiries.annotate(contact_last_name_lower=Lower('last_name'))
my_table = Hometable(inquiries)
Based on https://django-tables2.readthedocs.io/en/latest/pages/ordering.html#table-order-foo-methods you need to add a table.order_FOO() method and end up with something like this:
class Hometable(tables.Table):
contact_last_name = tables.Column(verbose_name="Contact", order_by=('contact_last_name'))
def order_contact_last_name (self, QuerySet, is_descending):
QuerySet = QuerySet.annotate(
field_lower=Func(F('contact_last_name '), function='LOWER')
).order_by(('-' if is_descending else '') + 'field_lower')
return (QuerySet, True)
class Meta:
model = Inquiry
fields= ('contact_last_name',)
This should work when you click column headers to order as well as the initial ordering.
I haven't tried this, and I'm not really familiar with django-tables2 so I don't know whether it will work.
You could try using a new field name e.g. contact_last_name_lower when setting order_by for the column.
class Hometable(tables.Table):
contact_last_name = tables.Column(verbose_name="Contact", order_by=('contact_last_name_lower',))
class Meta:
model = Inquiry
fields= ('contact_last_name',)
Then, when you instantiate the table, annotate the queryset with the lower case field.
queryset = Inquiry.objects.annotate(contact_last_name_lower=Lower('contact_last_name'))
table = Hometable(queryset)

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