Django models.Model class member not appearing in model_instance._meta.fields - python

I have a django.contrib.contenttypes.generic.genericForeignKeyField as a member of my model,
however, it is not appearing when I instantiate the model and then try to get the fields out of the _meta of the object.
e.g:
class A(models.Model):
field2 = models.IntegerField(...)
field1 = generic.genericForeignKeyField()
a = A()
a._meta.fields ---> this does not show field1, but shows field2.
Can someone please tell me why ?
Thanks !

Your are not setting up the generic relation correctly. Read the documentation:
There are three parts to setting up a GenericForeignKey:
Give your model a ForeignKey to ContentType.
Give your model a field that can store a primary-key value from the models you'll be relating to. (For most models, this means an IntegerField or PositiveIntegerField.)
This field must be of the same type as the primary key of the models that will be involved in the generic relation. For example, if you use IntegerField, you won't be able to form a generic relation with a model that uses a CharField as a primary key.
Give your model a GenericForeignKey, and pass it the names of the two fields described above. If these fields are named "content_type" and "object_id", you can omit this -- those are the default field names GenericForeignKey will look for.
In the end, it must be something like:
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')

Why would you expect it to? It's not a real field. It's a virtual field that's calculated using the (real) content_type and object_id fields on the model.
You can however see it in a._meta.virtual_fields.

Related

Any way to fetch the through fields for an object linked via Many2Many field without knowing the column name in advance?

I am trying to write a generic method that can take any Django Model and returns it in a dictionary form.
So for example, if my models are defined thus (very generic):
class A(models.Model):
somefieldA = models.TextField()
m2mfield = models.ManyToManyField(B, through='AandB')
def __unicode__(self):
return self.somefieldA
class B(models.Model):
somefieldB = models.TextField()
def __unicode__(self):
return self.somefieldB
class AandB(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
field1 = models.DecimalField()
field2 = models.TextField()
field3 = models.DateField()
Now, assume we have an instance of the object A a_obj.
I can get all the related B objects using:
# This loop is there because I am working with other fields as well.
def instance_to_dict(instance):
for field in instance._meta.get_fields():
if field.many_to_many:
m2m_mgr = getattr(instance, field.name)
for idx, assoc_obj in enumerate(m2m_mgr.all()):
assoc_obj_str = str(assoc_obj)
# How to obtain the related through field values?
# m2m_mgr.through.objects.get() would need prior knowlegde
# of field name so get(a=instance, b=assoc_obj) is not possible
# m2m_mgr.through.objects.all() fetches all the objects
# in the Many to Many manager.
And then call instance_to_dict(a_obj). This method could be called by passing other models' instances.
Ideally, I would like to create a dict of the obj and related "through" fields for any object. Is this possible to do?
In addition to the explicitly defined ManyToMany manager, there is also an implicit reverse relationship for the ForeignKey from AandB to A. So you can do something like this:
for field in instance._meta.get_fields(include_hidden=True):
if field.one_to_many: # reverse ForeignKey
m2m_through_mgr = getattr(instance, field.get_accessor_name()) # e.g. aandb_set
m2m_through_mgr.all() # all related instances from the through table
Another approach is to go through the through table fields looking at field.related_model to see which one points back to your original table.
This all gets quite messy, but there should be enough meta information to do what you want. One obstacle is that the API isn't fully documented. Specifically, relation fields are represented by instances of the ManyToOneRel class, which as of Django 2.1 remains undocumented for reasons hinted at in the source code. Hence my use of the undocumented get_accessor_name() method.

How to exclude some values in MultipleChoiceField Django

This is my model:
class MyModel(models.Model):
my_field = models.ManyToManyField(AnotherModel)
This is my view:
class MyModelView(UpdateView):
model = MyModel
Is it possible to exclude some values from multiple choice list in my view?
For example:
There are 10 positions in table AnotherModel but when updating MyModel I want only 5 values possible to choose.
You can use limit_choices_to argument to ManyToManyField.
It works exactly similar to limit_choices_to argument in ForeignKey. You can check out examples given in in ForeignKey documentation.

How can I choose the values that will be represented in a drop-down list in a form in django?

I am creating an application in django and I have the next question:
I have two models which are related as I show here:
class modelA(models.Model):
ident = models.AutoField(primary_key=True)
id_in_modelB = models.CharField(max_length=128, blank=True)
class modelB(models.Model):
attr1 = models.ForeignKey(modelA)
...
In the application, first I select the object of modelB I want to work with, and then, I show a form to select which object of modelA inside the selected modelB I want to select.
I created a form of modelB, and a drop-down list of objects of modelA appeared, but it has all the values of modelA inside the drop-down. And I want only to appear the objects that are related with the previously chosen modelA.
Is it possible?
Thank you!
It is not clear what exactly you mean with "previously chosen modelA", but you can provide a queryset for the form field. Assuming that formB is the modelform for modelb:
formB.fields['attr1'].queryset = modelA.objects.filter(...)
If it is not a modelform but a standard form:
formB.attr1.queryset = modelA.objects.filter(...)

Django 1.7: how to make ManyToManyField required?

I have a Django Model with ManyToManyField in it. I need to require user to select at least one M2M value in this field.
I tried to set blank=False to M2M field but it didn't help.
class Skill(models.Model):
name = models.CharField(max_length=200)
class PersonSkills(models.Model):
person = models.ForeignKey('Person')
skill = models.ForeignKey('Skill')
class Person(models.Model):
name = models.CharField(max_length=200)
skills = models.ManyToManyField('Skill', through='PersonSkills')
p = Person(name='Bob')
p.save()
# success, but I expect that this should throw ValidationError, because I didn't select at least one Skill for this person
I can solve this situation with custom Form definition or with override save() method for Person model.
Is it possible to prevent create Person without at least one Skill selected, with set ManyToManyField options? Or I need to create custom logic to handle this situation? Thanks.
I use Django 1.7 and Python 3.4
Update 1. How to create ModelForm to control M2M? Because in cleaned_data I have only fields that I pass for Person form, and haven't data that I pass as M2M fields. I try to create object in Admin Site and control that Skills selected. I enter Skill's via inline.
# admin.py
class PersonSkillsInline(admin.TabularInline):
model = Person.skills.through
extra = 2
class PersonAdmin(admin.ModelAdmin):
inlines = [PersonSkillsInline]
admin.site.register(Person, PersonAdmin)
On a database level... no, that's not possible. Any enforcement of this will have to come from your application logic.
The reason is that every m2m relation has a record with a foreign key to both sides of the m2m relation. SQL cannot enforce the existence of the referencing side of a relationship, only of the referenced side of a relationship.
Furthermore, you can't enforce it in your model either, because the Person has to be created and saved before you can assign any many-to-many relations.
Your only options are to enforce it in the form or the view.
In an InlineModelAdmin this can easily be done by specifying min_num (1.7+):
class PersonSkillsInline(admin.TabularInline):
model = Person.skills.through
min_num = 1
extra = 2

django model help involving m2m and foreignkey

Ok so this may be really easy for someone else to solve but i'm really confused on how to go about solving this.
So to start, i have a model A that has multiple fields that have many-to-many relationships to specific tables. So for example
class A(models.Model):
field1 = models.ManyToMany('field1Collection')
field2 = models.ManyToMany(field2Collection')
class field1Collection(models.Model):
description = models.TextField()
class field2Collection(models.Model):
description = models.TextFIeld()
Anyway this is what i'm trying to accomplish. I need to write another model that can hold a ranking system. So for example, i want to create a record where i can define
I have x number of ranks (3 for example):
field1Collection Object 3
field2Collection Object 6
field1Collection Object 2
So i basically want to be able to select objects from my field1Collection and field2Collection tables and assign them ranks. I tried thinking up schemes using foreignkeys and m2m fields but they all go wrong because the model needs to know "ahead" of time which collection set i need to reference. Does this many sense? can anyone help?
You can solve this using GenericForeignKey relationship
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class RankItem(models.Model):
rank = models.IntegerField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.rank
A normal ForeignKey can only "point to" one other model, which means that if the RankItem model used a ForeignKey it would have to choose one and only one model to store tags for. The contenttypes application provides a special field type which works around this and allows the relationship to be with any model
You need that field1Collection and filed2Collection have a common ancestor class which you can refer with a foreignKey. See django documentation on inheritance.

Categories

Resources