How to get properties of a model attribute? - python

Let's say that I have a class such as :
class MyClass(models.Model):
attributeA = models.CharField(max_length=100)
attributeB = models.IntegerField()
attributeC = models.CharField(max_length = 150, blank=True, nullable = True)
attributeD = models.ForeignKey('ModelB',related_name='FK_modelB')
attributeE = models.ManyToManyField('ModelC')
What I want to do is to get the properties of every attribute, not just the name that I got with :
my_instance._meta.get_all_field_name()
(which gave me a list of attributes names). No, what I want is, for every attribute, know what is his type (CharField,IntegerField, ForeignKey, ManyToManyField...), who's related if it's a ForeignKey / ManyToManyField and all the meta data such as max_length and so on.
The aim of it is to serialize a class into a XML and the representation in the XML will be different if it's a ManyToManyField, a ForeignKey or a simple value.
By the way, If anyone know a great class serializer to XML, it would help me a lot !
Thanks for your responses !

Django models _meta.fields is fields list that you can access to get field attributes:
>>> from django.contrib.auth.models import User
>>> u = User.objects.all()[0]
>>> u._meta.fields[1].__class__.__name__
'CharField'
>>> u._meta.fields[1].name
'username'
>>> u._meta.fields[1].max_length
30
>>> u._meta.fields[1].blank
False
# ...

You can get attributes of a specific field by using get_field()
MyClass._meta.get_field('attributeA').max_length

Related

Get '_set' for a many-to-many relationship with a through

I've got a user model like this:
class Person(AbstractUser):
id = models.AutoField(primary_key=True)
...(additional attributes taken out for brevity)...
kids = models.ManyToManyField(
'Person', through='Relationship', related_name='parents')
and a Relationship model that looks like this:
class Relationship(models.Model):
parent_id = models.IntegerField()
kid_id = models.IntegerField()
class Meta:
unique_together = ('parent_id', 'kid_id')
I'm trying to figure out the best way to get a set of the kids related to a particular parent (who would be the ones logged in).
I've got something like this:
user = Person.objects.get(id=request.user.id)
print(user.relationship_set.all())
But that gives me an error 'Person' object has no attribute 'relationship_set'
How best would I accomplish this?
I ended up going a slightly different route and using this filter:
Person.objects.filter(id__in=[ r.kid_id for r in Relationship.objects.filter( parent_id=person_id ) ]

How to set a specific queryset on a ObjectSerializerModel

I'm developing a web system using Django 1.11 and the current version of Django Rest Framework which is 3.8.2.
I'm having a problem while serving a JSON of my models in this project. I need to pass a JSON that contains a specific attribute.
I'm gonna exemplify my serializers.py to make an easier understanding of my problem.
class LikertSerializerModel(serializers.ModelSerializer):
class Meta:
model = Likert
fields = ('id', 'escala')
class RespostaSerializerModel(serializers.ModelSerializer):
likerts = LikertSerializerModel(many = True, read_only = True)
class Meta:
model = Resposta
fields = ('id', 'resposta','tipo', 'foto', 'pergunta', 'qtd_escolhida', 'classificacao_escala', 'data', 'likerts')
class PerguntaSerializerModel(serializers.ModelSerializer):
respostas = RespostaSerializerModel(read_only=True ,many=True)
class Meta:
model = Pergunta
fields = ('id', 'pergunta', 'tipo_questao', 'questionario', 'respondida', 'data_inicial', 'data_final',
'obrigatoria', 'outros', 'ordem', 'qtd_max_caracteres', 'respostas')
class QuestionarioSerializerModel(serializers.ModelSerializer):
entrevistadores = UsuarioSerializer(many = True, read_only = True)
sub_administrador = UsuarioSerializer(read_only= True)
perguntas = PerguntaSerializerModel(many = True, read_only = True)
class Meta:
model = Questionario
fields = ('id', 'titulo', 'descricao', 'data', 'duracao', 'localizacao', 'sub_administrador', 'entrevistadores', 'perguntas')
THE PROBLEM: See, in the PerguntaSerializerModel I have this field "respostas". And the thing is, I only want to pass to that Pergunta Object, Resposta Objects that have a "tipo" attribute with a value of "alt" and are linked with that Pergunta Object.
PS: If you need an explanation of how those classes are linked, here it goes (we can ignore the Likert Object as it is useless to the problem):
A Resposta Object is linked by a ForeignKey to a single Pergunta Object, and a Pergunta Object is linked by a ForeignKey to a single Questionario Object.
A Questionario Object can have one or more Pergunta Objects and a Pergunta Object can have one or more Resposta Objects
When you do a GET in the rest URL the final JSON will start with the Questionario Object, and will cascade until the last Pergunta Object.
Please help me to find an answer, I wasn't able to find one in the REST documentation because I don't know what I need to look for.
Best regards.
What you need here is a SerializerMethodField(). You can read through the docs for more info on it.
Create a method on the serializer PerguntaSerializerModel that returns a filtered queryset of Resposta objects on the basis of attribute tipo.
Something like this should work fine:
class PerguntaSerializerModel(serializers.ModelSerializer):
respostas = serializers.SerializerMethodField()
class Meta:
model = Pergunta
fields = ('id', 'pergunta', 'tipo_questao', 'questionario', 'respondida', 'data_inicial', 'data_final',
'obrigatoria', 'outros', 'ordem', 'qtd_max_caracteres', 'respostas')
def get_respostas(self, obj):
reposta_qs = obj.filter(reposta__tipo='alt')
resposta_serailizer = RespostaSerializerModel(reposta_qs, read_only=True ,many=True)
return resposta_serailizer.data
Let me know if this helps !

django-tables 2 M2M field not shown

I am trying to show a M2M field in a django-table2 as seen in Django-tables2: How to use accessor to bring in foreign columns? and Accessing related models with django-tables2
Using: foreigncolumn = tables.Column(accessor='foreignmodel.foreigncolumnname'), I only see a '--'...
# The models:
class Organism(models.Model):
species_name = models.CharField(max_length=200)
strain_name = models.CharField(max_length=200)
eukaryotic = models.BooleanField(default=True)
lipids = models.ManyToManyField('Lipid',blank=True)
class Lipid(models.Model):
lm_id = models.CharField(max_length=100)
common_name = models.CharField(max_length=100,blank=True)
category = models.CharField(max_length=100,blank=True)
#The tables
class OrganismTable(tables.Table):
name = tables.LinkColumn('catalog:organism-detail', text=lambda record: record.species_name, args=[A('pk')])
lp = tables.Column(accessor='Lipid.common_name')
class Meta:
model = Organism
sequence = ['name','lp']
exclude = ['id','species_name']
Any idea what I'm doing wrong?
This does not work so easily for ManyToManyFields because of the simple way Accessor works. You could display the repr of the related QuerySet via 'lipids.all' but that does not seem sufficient here. You can, however, add a property (or method) to your Organism model and use it in the accessor. This way, you can display any custom information related to the instance:
class Organism(models.Model):
# ...
#property
def lipid_names(self):
return ', '.join(l.common_name for l in self.lipids.all()) # or similar
class OrganismTable(tables.Table):
# ...
lp = tables.Column(accessor='lipid_names')
I would recommend then to add a prefetch_related('lipids') to the Organism QuerySet that you pass to the table for better performance.

ManyToMany Relation using Django

I have the following code on models.py
class Movie(models.Model):
mov = models.CharField(max_length = 500)
def __str__(self):
return self.mov
class Atrib(models.Model):
atrib = models.CharField(max_length = 500)
def __str__(self):
return self.atrib
class Dict(models.Model):
atrib = models.ForeignKey(Atrib)
dictionary = models.ManyToManyField(Movie, related_name = 'dictionary')
I want to load a defaultdict like this: {attribute1:[movie1, movie2, ...], atribute2:[movie2, movie3,...] ... } using Django.
First of all, I added all the attributes of the dictionary using the Atrib class.
Now I need to add, for every attribute, the related movies. I tried to do the following, considering only one attribute (just testing):
attribute1 = Atrib(atrib = atribs)
for m in dictionary[attribute]:
m = Movie(mov = m)
m.save()
attribute1.dictionary.add(m)
It shows me the following error:
AttributeError: 'Atrib' object has no attribute 'dictionary'.
Am I doing the models correctly for this type of dictionary? And why is this error occurring? I am new in Django and I am a little lost!
Thanks in advance!
First of all you need to save attribute1 = Atrib(atrib = atribs)
do attribute1.save()
Next, after Attribute object creation you don't have Dictionary object related to it.
You need to create it first, set it to this object and then you will have ability to operations you want.
Note: to access attribute.dict you need OneToOne relationship, not ForeignKey.

django-contenttypes - List All Generic Relations for Model

I would like to reflect on a model and list all its backward generic relations.
My model looks like this:
class Service(models.Model):
host = models.ForeignKey(Host)
statuses = generic.GenericRelation(Status)
The Status object looks like this:
class Status(TrackedModel):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
class Meta:
verbose_name_plural = 'statuses'
I would like to programatically learn that statuses is a generic relation for the Service model. Is this possible? Status._meta.fields does not show statuses, but Status._meta.get_all_field_names() does, only it shows other unwanted things too.
I thought that this might be a possible solution, but it seems really messy to me. I'd love to hear of a better one.
from django.db.models.fields import FieldDoesNotExist
from django.contrib.contenttypes import generic
generic_relations = []
for field_name in Service._meta.get_all_field_names():
try:
field = Service._meta.get_field(field_name)
except FieldDoesNotExist:
continue
if isinstance(field, generic.GenericRelation):
generic_relations.append(field)
Thank you!
The GenericRelation works similarly as ManyToManyField. You could find it in Service._meta.many_to_many:
filter(lambda f:isinstance(f, generic.GenericRelation), Service._meta.many_to_many)
UPDATE 2021:
To list all the GenericRelations() fields:
print(Service._meta.private_fields)
Output:
[<django.contrib.contenttypes.fields.GenericRelation: statuses>]
Nevertheless, if you have more fields with GenericRelations() relationship they will be shown into the output list.
Check the documentation:
https://docs.djangoproject.com/en/3.2/releases/1.10/#id3
Or you can return all of the fields that have a GenericRelation() field type.
ex:
# models.py
class MyModel(models.Model):
. . .
my_model_field = GenericRelation(OtherPolyModel)
def get_generic_relation_fields(self):
"""
This function returns all the GenericRelation
fields needed to return the values that are
related to a polymorphic model.
"""
fields = [f.attname for f in self.Meta.model._meta.get_fields()]
file_fields = []
for field in fields:
get_type = self.Meta.model._meta.get_field(field)
field_type = get_type.__class__.__name__
if field_type == "GenericRelation":
file_fields.append(field)
return file_fields
Output:
['my_model_field']

Categories