django model help involving m2m and foreignkey - python

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.

Related

filter django queryset by onetoone model field property

i have two model classes and they are related by OneToOneField like here
class Book(models.Model):
is_user_ok = models.BooleanFiled()
class Mybook(models.Model):
book = models.OneToOneField( Book, related_name="Book", null=True, blank=True, on_delete=models.SET_NULL)
now i want make a queryset filter by book field property. but here book.is_user_ok
cannot be used. how can i make this queryset filter?
queryset = Mybook.objects.filter(book.is_user_ok=True)
You are looking a common problem, what you really want to do is related_name__column_name in filter, in your case you can try
queryset = Mybook.objects.filter(book__is_user_ok=True=True)
Reference from official docs:
https://docs.djangoproject.com/en/4.0/topics/db/queries/
double underscore is used for joins as you have already set the relationship in model variety of joins work here, it is used as per the model relation.
You are using a wrong syntax. Replace (book.is_user_ok=True) with (book__is_user_ok=True) The problem is, using '.' instead of '__'. Thats the correct syntax when dealing with another model class

Inheritance model update to its parent model

I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.

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

Generic many2many relationships in Django

So, I have the 4 entities represented below which are strong and independent entities on my application, now the problem is that each Article or Picture could be "tagged" with a Presenter or an Event, being as they are the 4 of them independent entities that could become more complex It doesn't look right to add Event and Presenter field to both Article and Picture or the contrary, specially because they could be tagged with none.
In the long run as well other entities might need to be tagged and other taggable entities might appear.
class Article(models.Model):
#Fields
class Picture(models.Model):
#Fields
class Presenter(models.Model):
# Fields
class Event(models.Model):
# Fields
The closer I am getting is to some kind of double-headed Generic contenttype based intermediate model like this(haven't tested yet as it is a bit more complex than that), but I am looking for ideas:
class GenericTag(models.Model):
# Event,Presenter instance..
tagcontent_type = models.ForeignKey(ContentType)
tagobject_id = models.PositiveIntegerField()
tagcontent_object = generic.GenericForeignKey('tagcontent_type', 'tagobject_id')
# Picture,Article instance
objcontent_type = models.ForeignKey(ContentType)
objobject_id = models.PositiveIntegerField()
objcontent_object = generic.GenericForeignKey('objcontent_type', 'objobject_id')
And with that just do queries based on the information I have, I think there have to be more elegant ways to do this without stuffing all tagmodels as fields into taggablemodels.
A different approach without generic foreign keys would be to use model inheritance:
class Tag(models.Model):
pass
class Taggable(models.Model):
pass
class Article(Taggable):
# Fields
class Picture(Taggable):
# Fields
class Presenter(Tag):
# Fields
class Event(Tag):
# Fields
class TagInstance(models.Model):
tagged = models.ForeignKey(Taggable)
tag = models.ForeignKey(Tag)
This introduces additional tables for the two base classes and shared primary-key spaces for their respective instances, so they can be referenced with ordinary foreign keys. This might be reasonable depending on what further you want to do with your tags.

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

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.

Categories

Resources