Django using a ManytoMany as a Foreign Key - django

I'm trying to use a many to many relation as a foreign key for another table, but I'm not sure if this is possible.Consider the following model:
from django.db import models
class Attribute(models.Model):
name=models.CharField(max_length=30)
unit=models.CharField(max_length=30)
class Protocol(models.Model):
name=models.CharField(max_length=30)
attributes=models.ManyToManyField(Attribute,db_table='protocol_attribute')
class Device(models.Model):
serial_number = models.CharField(max_length=30)
id_protocol=models.ForeignKey(Protocol)
class DeviceValues(models.Model):
id_device=models.ForeignKey(Device)
id_protocol_attribute=models.ForeignKey(Protocol.attributes)
value=models.IntegerField()
When I try to validate this model I receive the following error:
First parameter to ForeignKey must be either a model, a model name, or the string 'self'
I know I am not using a model or a model name as the first parameter, but how can I reference as a foreign key the join table that Django will automatically create that handles the mapping between Protocol and Attributes?
A possible workaround could be to add the protocol as a foreign key to the Attribute class and then use the attribute as a foreing key to the DeviceValue class. However, I don't want to do that because I will have protocols that will be sharing attributes. I would prefer to make the join table rather that repeating attributes for each protocol.
Any ideas will be more than welcome....thanks!!!
Alejandro

Using an intermediate table, I solved it. The model ended up like this.
from django.db import models
class Attribute(models.Model):
name=models.CharField(max_length=30)
unit=models.CharField(max_length=30)
class Protocol(models.Model):
name=models.CharField(max_length=30)
attributes=models.ManyToManyField(Attribute,through='DeviceValue')
class Device(models.Model):
serial_number = models.CharField(max_length=30)
id_protocol=models.ForeignKey(Protocol)
class DeviceValue(models.Model):
id_name=models.ForeignKey(Protocol)
id_attribute=models.ForeignKey(Attribute)
id_device=models.ForeignKey(Device)
value=models.IntegerField()
One thing worth mention is that when using sqlall the UNIQUE constraint for the three foreign keys of the DeviceValue tablue was not generated, so I just added it before creating the database.

Related

Is there a way to create a django model foreignkey field with variable choices?

Let's say I have three models device, mechanical and digital. In the device model I have a field type. The type field needs to be in a foreign key relationship with either mechanical or digital model which will be determined by the data. Is there any way to create the type field in such a way that the model in which the foreign key relationship is to be done can be chosen manually.
Something like:
type = models.ForeignKey(to=Choices)
where the Choices could be digital and mechanical.
I have tried implementing generic foreign keys but my database schema is a bit complex so that would be very difficult to maintain. Is there any other way in django that I can do the above?
A generic way that you can manage this would be to use your own models.
Create a ChoiceItemGroup model, slug, name, description.
Create another model ChoiceItem, with slug, name, description, and FK to ChoiceItemGroup.
Then you can do:
type=models.ForeignKey(ChoiceItem, on_delete=models.CASCADE)
Then you can create generic choices and generic choice groups by registering the models in the admin panel
Adding a foreign key from one of your own models to ContentType allows your model to effectively tie itself to another model class, But it’s possible to go one step further and use ContentType to enable truly generic (sometimes called “polymorphic”) relationships between models.
For example, it could be used for a tagging system like so:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType,
on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return self.tag
Django generic relations

Including fields from a OneToOneField in Django Admin

I am attempting to add the fields from a OneToOneField into my admin view. Here is an example of how my models look.
class Customer(BaseUser):
name = CharField()
address = CharField()
secondary_information = OneToOneField("SecondaryCustomerInfo", on_delete=SET_NULL, null=True)
class SecondaryCustomerInfo(models.Model):
email = EmailField()
And I tried adding in the fields as an inline like this.
class SecondaryCustomerInfoInline(admin.StackedInline):
model = SecondaryCustomerInfo
class CustomerAdmin(admin.ModelAdmin):
inlines = [SecondaryCustomerInfoInline]
But I get the error
<class 'user.admin.SecondaryCustomerInfoInline'>: (admin.E202) 'user.SecondaryCustomerInfo' has no ForeignKey to 'user.Customer'.
I'm used to putting the OneToOneField on the secondary model but my coworker asked that I put it on the main Customer model since we will be accessing that information more often. I think switching things around is what is tripping me up. How would I include the fields from SecondaryCustomerInfo on the admin view for Customer?
The answer would be to use Django Reverse Admin
From its documentation:
Module that makes django admin handle OneToOneFields in a better way. A common use case for one-to-one relationships is to "embed" a model inside another one. For example, a Person may have multiple foreign keys pointing to an Address entity, one home address, one business address and so on. Django admin displays those relations using select boxes, letting the user choose which address entity to connect to a person. A more natural way to handle the relationship is using inlines. However, since the foreign key is placed on the owning entity, django admins standard inline classes can't be used.
class CustomerAdmin(ReverseModelAdmin):
inline_type = 'stacked'
inline_reverse = ['secondary_information']

Django using reverse OneToOne in filter?

I have two models:
class CustomUser(models.Model):
...
class Profile(models.Model):
user = models.models.OneToOneField(CustomUser)
fielda = models.IntegerField()
I'm trying to perform a filter query based on the value of a field on the Profile model from the CustomUser model but it's giving me the error name 'profile' is not defined
CustomUser.objects.all().select_related('profile').filter(profile.fielda=0)
I need to start the selection from CustomUser model because I have to prefetch other models with FK to CustomUser so I cannot simply start from Profile model
One uses two consecutive underscores (__) to look "through" a relation, so you can filter with:
CustomUser.objects.filter(profile__fielda=0)
The above will thus retrieve all CustomUsers with a related Profile object where fielda is 0.
Note that the .select_related(..) is not necessary for filtering. This is only used if you want to fetch the data into memory as well.

Add a unique "intermediary" model to all my Django ManyToManyField()

Hello Awesome People!
Such a question that I have made a lot of searches for it. I am done building a website two(2) months ago, but today The team decides to track every time an instance has been added to a Model in ManyToManyField() fields.
I was thinking using the through argument to point to the model that will act as an intermediary may work but not at all in my case (70%). Just Because I want to have a unique intermediary model that will record for all ManyToManyField()
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateTimeField(auto_now_add=True)
Ah! Something is required. I need to explicitly specify foreign keys to the models that are involved in the many-to-many relationship.
Django ContentType may anticipate for all my models, but it's not working, I wonder why? it also contains ForeignKey (the one required by an intermediary model).
Do I really need to edit all my ManyToManyField fields and create Model as much as ManytoManyField? is there a way to record date_joined without creating an intermediary model for each?
Are you perhaps looking for something like django admin's LogEntry model?
LogEntry contains the ContentType of the model instance that has changed, the id of the instance, the type of change and an abstract change message. With all of that you can retrace changes made to instances.
In django admin, the views take care of adding records to LogEntry via three methods log_change/addition/deletion: click.

Django models: database design for user and follower

In Django model I am making a table 'followers', which has:
user's id. (this is followed by)
user's id (this is follower)
that's simple a user can follow other users.
How should I define the model in Django?
I tried this, but does not work:
user = models.ForeignKey('self')
follower_id = models.ForeignKey('self')
How should this be done?
thanks
The 'self' argument won't work unless you have a model called self.
Assuming that your assignment model is called Following, and you're using the built in User model then you can do:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers')
follower = models.ForeignKey('User', related_name='targets')
This will likely need some further uniqueness and validation logic.
Note the related_name attribute, see https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_name. This means that for a given user object you can do user.targets.all() to get users they follow, and user.followers.all() to get users who follow them.
Note also that Django returns target model instances, not IDs, in the ORM. This means that even though the underlying table may be called follower_id, in the python code following.follower will return an actual User object.
Seeing as Following is actually the through table for the many-to-many relationship between Users. I would create a Profile model which extends the Django User model, and then declare the many-to-many relationship (using ManyToManyField).
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
following = models.ManyToManyField(User, related_name='followers')
Use the many to many field.
followers = models.ManyToManyField('self', symmetrical=False)