approaching django model fields through a field name mapping - python

Django newbie here,
I have several types of models, in each of them the fields have different names (e.g. first_name, forename, prenom) and I want each of the models to contain a mapping so that I can easily approach each of the fields using one conventional name (e.g. first_name for all of the field names). what's a good way of doing this?

I think the best way would be to use conventional names in your models and provide only one obvious way to access it. If you don't wan't to change the database columns too, you can use the db_column option. Example:
class Person(models.Model):
first_name = models.CharField(max_length=255, db_column='prenom')
class Customer(models.Model):
first_name = models.CharField(max_length=255, db_column='forename')
class Worker(models.Model):
first_name = models.CharField(max_length=255) # column is also called "first_name"
If you need to provide different ways to access the members (which I would try to avoid!) you can still add properties to each model.

You could define a #property on each of your models, like this:
class Personage(models.Model):
prenom = models.CharField(max_length=255)
#property
def first_name(self):
return self.prenom
then you can just reference your new property like this:
personage = Personage.objects.all()[0]
personage.first_name

You could make your models all inherit a new model that has a property function defined to get/set the right variable.
class BaseNameClass(models.Model)
def getfname(self):
if hasattr(self, 'first_name'): return self.first_name
if hasattr(self, 'prenom'): return self.prenom
if hasattr(self, 'forename'): return self.forename
def setfname(self, x):
if hasattr(self, 'first_name'): self.first_name = x
if hasattr(self, 'prenom'): self.prenom = x
if hasattr(self, 'forename'): self.forename = x
firstname = property(getfname, setfname)
And then change your models to all inherit from that. It will be slightly slower but we're talking nano and milliseconds.
If you had an descendant object called person, you'd access the name simply by:
print person.firstname
person.firstname = "oli"
print person.firstname

Related

Django REST Framework: how to dynamically change a field name when inheriting

I want to create a base class that will be used by other classes. Only one field name will change depending on the child class.
class Person(serializers.Serializer):
name = serializers.CharField()
other_fields = ...
def validate(self, attrs):
# something to validate the name and other fields together
...
class John(Person):
john_name = Person.name # ?
All I want is to be able to receive a payload like this: {"john_name": "john"}, without the need to redo the Person validations for every child class of Person that the code needs.
How do I do this?
is this what you are looking for?
class John(Person):
john_name = serializer.charField(write_only=True)
def create(self,validated_data):
person_name = validated_data.pop('john_name')
validated_data["person_name"] = person_name
return Super().create(validated_data)

Property of a self instance in Django Models?

recently i have created a model for storing some states in my DB. It's very simple i only store id and name.
class PayType(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
class Meta:
ordering = ('-name',)
def __str__(self):
return "[{0}] {1}".format(self.id, self.name)
Here you can see a migration where i insert default values in the DB.
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('stockmanager', '0023_pagotipo_remove_carrito_unidades_totales_and_more'),
]
def insertPayType(apps, schema_editor):
PayType = apps.get_model('stockmanager', 'PayType')
PayType(name="Credit").save()
PayType(name="Debit").save()
PayType(name="Cash").save()
operations = [
migrations.RunPython(insertPayType)
]
As you can see, i'll have fixed rows in DB.
I'd like to add properties in the PayType Model, in order to have easy access the instances of the model. Like a static attribute in java.
class PayType(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
class Meta:
ordering = ('-name',)
def __str__(self):
return "[{0}] {1}".format(self.id, self.name)
# I'm looking for something like this
#property
def CREDIT(self):
return self.objects.get(id = 1)
# I also tried with this, same result :(
#property
def DEBIT(self):
return get_object_or_404(self, id = 2)
I tried to access in a diferent part of my code with this
class Pay(models.Model):
id = models.AutoField(primary_key=True)
order_id = models.IntegerField()
value = models.FloatField()
pay_type = models.ForeignKey(PayType, on_delete=models.CASCADE)
Pay(
order_id = order_id,
value = 100,
# I want this
pay_type = Pay.CASH #Error line
).save()
The thing is that i receive this error
Cannot assign "<property object at 0x00000138FEAC7F60>": "Pay.pay_type" must be a "PayType" instance.
Any idea?
Can't you just write your logic this way? It should work as indeed:
# I'm looking for something like this
#property
def CREDIT(self):
return PayType.objects.get(id = 1)
# I also tried with this, same result :(
#property
def DEBIT(self):
return get_object_or_404(PayType, id = 2)
You could also try to fetch PayType class using meta framework or try to catch it using type(self), I think it should work.

Django models Many to one not able to access all objects in a foreign key field

I might be confused however when I check the django-admin panel I can see (will provide a screenshot) , over 50 models attached to my primary model, however whenever I make a query in the code and try to access the set associated with the field I only ever get one entry. I am trying to make one query and check all models associated with the field.
My models.py code:
class TokenData(models.Model):
name = models.CharField(max_length=200)
contract_address = models.CharField(max_length=200,primary_key=True, unique=True)
def save(self, *args, **kwargs):
#print('save() is called.')
super(TokenData, self).save(*args, **kwargs)
def __str__(self):
return self.name
class WalletData(models.Model):
address = models.CharField(max_length=200,primary_key=True,unique=True)
contract_address = models.ForeignKey(to=TokenData,on_delete=models.CASCADE,)
last_bitquery_scan = models.CharField(max_length=200)
def __str__(self):
return self.address
I am trying to access the model like so :
WalletData.objects.filter(address=address)
One thing I noticed is when I create a variable containing the filter, and access the contract_address in the WalletData model, I can endlessly query myself in a circle for lack of a better word, by accessing the set and executing a get against it.
I am just wanting to access all 50 models like shown below
The 50+ objects you see in the drop down are just all the TokenData objects in the DB listed by the name.
From what i can understand what you want is all the TokenData associated with a particular WalletData address, if so then for a WalletData object w you can do
TokenData.objects.filter(contact_address = w.contact_address).values_list('name',flat=True)
This gives you all the TokenData name associated to a WalletData address.
I ended up figuring it out here was my fix:
Models.py
class TokenData(models.Model):
name = models.CharField(max_length=200)
contract_address = models.CharField(max_length=200,primary_key=True, unique=True)
def save(self, *args, **kwargs):
#print('save() is called.')
super(TokenData, self).save(*args, **kwargs)
def __str__(self):
return self.name
class WalletData(models.Model):
address = models.CharField(max_length=200,primary_key=True,unique=True)
#contract_address = models.ForeignKey(to=TokenData,on_delete=models.CASCADE,)
last_bitquery_scan = models.CharField(max_length=200)
def __str__(self):
return self.address
class WalletHolding(models.Model):
token = models.ForeignKey(to=TokenData,on_delete=CASCADE)
wallet = models.ForeignKey(to=WalletData,on_delete=CASCADE)
def __str__(self):
return self.token
I had to add another model i.e WalletHolding, this way when I query the WalletHolding by the wallet field I get all tokens associated with the wallet using a query like this:
WalletHolding.objects.filter(wallet=address).values_list('token',flat=True)
Result:

Django filter on values of child objects

I have the following (simplified) data model:
class Article(Model):
uuid = models.CharField(primary_key=True, max_length=128)
class Attribute(Model):
uuid = models.CharField(primary_key=True, max_length=128)
article = models.ForeignKey(Article, related_name='attributes')
type = models.CharField(max_length=256)
value = models.CharField(max_length=256)
An example usage would be an article with an attribute attached to it with type="brand" and value="Nike". Now I want to write an API which can get all articles with a certain brand, but I can't seem to write the filter for it. This is what I have so far:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
class Meta:
model = Article
def filter_brand(self, queryset, name, value):
return queryset.filter('order__attributes')
class PhotobookViewSet(AbstractOrderWriterViewSet):
queryset = Article.objects.all()
serializer_class = ArticlePhotobookSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_class = PhotobookFilter
The line with queryset.filter is obviously not correct yet. I need to create a filter here that returns all articles that contain an attribute with type="brand" and value=value. How would I do this?
Are you sure you want to condense both lookups (type and value of Attribute) into one filter? Why not allow filtering on both fields separately?
E.g.
class PhotobookFilter(df.FilterSet):
type = df.CharFilter(method='filter_type')
value = df.CharFilter(method='filter_value')
class Meta:
model = Article
def filter_type(self, queryset, name, value):
return queryset.filter(**{'attributes__type': value})
def filter_value(self, queryset, name, value):
return queryset.filter(**{'attributes__value': value})
And now a query like ?type=brand&value=Nike should work.
Obviously you could condense both conditions into one filter and for example hard code the band part:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
def filter_brand(self, queryset, name, value):
return queryset.filter(**{'attributes__type': 'brand', 'attributes__value': value})
But keeping them separate feels way more flexible.
You could also filter in reverse like this:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
class Meta:
model = Article
def filter_brand(self, queryset, name, value):
articles = Attribute.objects.filter(type="brand", value=value).values_list('article_id', flat=True)
return queryset.filter(id__in=articles)
This will create subquery for Attribute, which will still be one sql request in the end
Use search_fields .For correct result rename your 'type' attribute name http://www.codesend.com/view/09ca65d42248fe1d89d07ce151f4f050/

Django rest framework represent flatten nested object

I have a parent and a one-to-one related child model and I would like to render the fields from the child flat in the parent representation (read only). Currently, I have achieved that with a custom to_representation implementation but that seems very involved and I wonder if there is no easier way to achieve this.
It is made more complicated by the fact that my related model is connected via a property.
So here is the concrete example:
By default a related object would be rendered like:
{
parent_name:'Bob',
child:{
name:'Alice'
}
}
This is what I want and currently get with my to_representation:
{
parent_name:'Bob',
child_name:'Alice'
}
My models look like this:
class ChildModel(models.Model):
name = models.CharField(max_length=100, null=True)
class ParentModel(models.Model):
name = models.CharField(max_length=100, null=True)
_child = models.ForeignKey('ChildModel', null=True)
#property
def child(self):
return self._most_recent_status
#name.setter
def child(self, value):
self._child = value
Here are my serializers:
class FlatChildField(serializers.RelatedField):
def to_representation(self, value):
return value.name
class FlatParentSerializer(serializers.ModelSerializer):
parent_name = serializers.CharField(source='name', read_only=True)
child_name = FlatChildField(source='_child', read_only=True)
class Meta:
model = Parent
fields = ('name', 'child_name')
For a simpler solution to get a flat representation of related models I would be grateful.
For completeness, I would be interested to hear if there is a simpler solution for "normal" related models (i.e. not property model fields as well). I was looking for the equivalent of the django model query syntax of related_model__field, but I cannot find that. Does that exist for django rest framework?
Many thanks
The simplest means would be to use source:
class FlatParentSerializer(serializers.ModelSerializer):
parent_name = serializers.CharField(source='name', read_only=True)
child_name = serializers.CharField(source='_child.name', read_only=True)
class Meta:
model = Parent
fields = ('name', 'child_name')
You can use SerializerMethodField, it saves you really a lot of work and it's so clean and trivial:
class FlatParentSerializer(serializers.ModelSerializer):
parent_name = serializers.CharField(source='name', read_only=True)
child_name = serializers.SerializerMethodField('get_child_name')
class Meta:
model = Parent
fields = ('name', 'child_name')
def get_child_name(self, obj):
return obj._child.name

Categories

Resources