Django: Model Design, ManyToMany attribute fields? - sql

Pretty new to working with databases in this way. I've got some sample code below. I've got the instrument object which will be a db listing of types of instruments, guitar, piano etc. Then the user object will have a ManyToMany on that so each user can have as many of those listed in their profile as they play.
What I'm stuck on is I'd like to have a field for experience with each of those instruments. Just not sure how to accomplish this without just static fields for how many instruments there would be (which since it's modifiable, could change). Thanks for any pointing in the correct direction.
class Instrument(models.Model):
# Name of the instrument
name = models.CharField(_('Name of Instrument'), blank=True, max_length=255)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
#python_2_unicode_compatible
class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = models.CharField(_('Name of User'), blank=True, max_length=255)
zipcode = models.IntegerField(_('Zip Code of the User'), blank=True, null=True)
instruments = models.ManyToManyField(Instrument)

Seems like a textbook use case for a through model with extra fields.
class InstrumentExperience(models.Model):
user = models.ForeignKey('User')
instrument = models.ForeignKey('Instrument')
experience = models.CharField(max_length=100)
class User(AbstractUser):
...
instruments = models.ManyToManyField('Instrument', through='InstrumentExperience')

Related

FOREIGN KEY constraint failed Django Models

I'm creating a small-ish django application using AllAuth for the authetncation, which I have customised myself to include some additional fields.
Part of the sites functionality requires me to refrence the logged in user through a Foreign Key multiple times and I'm approaching this through the custom model I created, called UserProfile; (I tried Django's User model & this also gave me errors)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
phone_number = models.CharField(max_length=30, null=False)
postcode = models.CharField(max_length=500, null=True, blank=True)
town_or_city = models.CharField(max_length=40, null=True, blank=True)
street_address1 = models.CharField(max_length=80, null=True, blank=True)
street_address2 = models.CharField(max_length=80, null=True, blank=True)
county = models.CharField(max_length=80, null=True)
I'm referencing the above model in the activity table:
class Activity(models.Model):
class Meta:
verbose_name_plural = 'Activities'
activity_id = models.CharField(max_length=32, null=False, editable=False)
host = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
name = models.CharField(max_length=254, null=False, blank=False)
date = models.DateField()
start_time =models.TimeField()
end_time = models.TimeField()
duration = models.DurationField(blank=True, null=True)
location = models.CharField(max_length=40, null=False, blank=False)
description = models.CharField(max_length=140, null=False, blank=False)
available = models.BooleanField(default=True)
Everything works smoothly, as I can use this to create one activity per user, however I want to make this a Many to One field, which required me to update from settings.AUTH_USER_MODEL to the Foreign Key.
Unfortunately, I'm getting the following error:
FOREIGN KEY constraint failed
when I try to add a new activity now, and I'm at a loss as to why this is happening.
If someone could point me in the right direction on how to create this many to one functionality, it would be great.
I can't see your whole code so the answear may be wrong but it Looks like you dont have Primary Key in UserProfile
ForeignKey As Default takes Primary Key as argument
Potential solve:
class Activity(models.Model):
host = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile', primary_key=True)
If it dosent solve your problem please put full error message
DOCS
I would guess, from what information that you've provided, is that what has happened is that you've potentially got a user who doesn't have a user profile, so when you try to create the activity it fails with the error you specified. On the other hand, it could also be related to how you're rendering and validating your forms. Furthermore it could also be due to the way you are using class based views views or function based views.
In any case, please provide all information possible. Generally all information is required, not just the part you're unsure of.
How do you create an activity?
How do you validate your forms?
How is the user profile ID populated?
Providing more information, I will happily respond as it seems like a straight forward question

Django models ManyToManyField always adding item to list

I'implemented two models, Card and Dish. I used many-to-many relationship because Dish can be in many cards. Not sure if I did that right because any Dish that I add is automatically added to every Card.
Here is the code:
class Dish(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=1000)
price = models.DecimalField(max_digits=5, decimal_places=2)
preparation_time = models.IntegerField()
date_added = models.DateField(auto_now_add=True)
update_date = models.DateField(auto_now=True)
vegan = models.BooleanField(default=False)
def __str__(self):
return self.name
class Card(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=1000)
date_added = models.DateField(auto_now_add=True)
update_date = models.DateField(auto_now=True)
dishes = models.ManyToManyField(Dish, blank=True)
def __str__(self):
return self.name
An the problem in admin panel looks like this:
When I create a dish it is alway added to every card.
Any help please I'm just begining to learn sql and django ORM
Dishes being in that admin field doesn't mean they're actually selected, that's just the Multi-Select field. Only the ones that are highlighted are actually in the field. and you do +Click to toggle if they're selected or not
I guess that was the best way to show a Many-to-Many field, tho it might be confusing to use imo.. That's why I always just edit them in the shell, especially when there gets 100+, 200+ items.

The best way to avoid generic ForeignKey in django

I have Picture model which contains different link to images. I also have People and Car models that may have one or more images. That means that certain pictures can belong to an object in Car or People model.
I try to make ForeignKey but one of the field (car_id or people_id) will be empty.
I can't create an abstract model and make ForeignKey from Picture
The last solution that I know is genericForeignKey but it seems to complex for such trivial task.
Is there are a best way to solve the problem?
I solved the problem like this:
I created another model called Album that has only id
class People(models.Model):
name = models.CharField(max_length=100)
album = models.OneToOneField(Album)
class Car(models.Model):
horse_power = models.IntegerField()
ablum = models.OneToOneField(Album)
class Picture(models.Model):
title = models.CharField(max_length=100)
album = models.ForeignKey(Album)

sqlalchemy symmetric many to one friendship

I am attempting to model a friendship using SQLAlchemy ORM. The relationship that I am trying to model is symmetric. Similar to Facebook, if user a is to add user b, user b must approve that friendship request. My current model is as follows.
class User(db.Model):
__tablename__ = 'User'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(35), unique=False)
username = db.Column(db.String(25), index=True, unique=True)
password = db.Column(db.String(35), unique=False)
email = db.Column(db.String(35), unique=True)
phone_number = db.Column(db.String(22))
# define relationships
requester = db.relationship('Relationship', foreign_keys='Relationship.requesting_user', backref='requester')
receiver = db.relationship('Relationship', foreign_keys='Relationship.receiving_user', backref='received')
def __repr__(self):
return '<User %r>' % (self.username)
class Relationship(db.Model):
__tablename__ = 'Relationship'
id = db.Column(db.Integer, primary_key=True)
requesting_user = db.Column(db.Integer, db.ForeignKey('User.id'))
receiving_user = db.Column(db.Integer, db.ForeignKey("User.id"))
status = db.Column(db.Integer)
__table_args__ = (db.UniqueConstraint('receiving_user', 'requesting_user', name='_receiving_user_uc'), )
The model works, however, I don't think that it is properly modeled. Is it even required that I use a status? I'm assuming it can be modeled so that each friend relationship gets its own entry. Currently, a user can initiate a friend request with another user. When the other user approves the request, the status changes to accepted. I have looked a little into association tables but am not too sure how they would play into a model like this. Any advice on my current model and how it can be improved would be greatly appreciated.
Among other things, you may want to learn about association proxies. An association proxy tells SQLAlchemy that you have a many-to-many relationship mediated by an intermediate table which may contain additional data. In your case, each User can send multiple requests and also receive multiple requests and Relationship is the mediating table which contains the status column as additional data.
Here is a variant of your code which stays relatively close to what you wrote:
from sqlalchemy.ext.associationproxy import association_proxy
class User(db.Model):
__tablename__ = 'User'
# The above is not necessary. If omitted, __tablename__ will be
# automatically inferred to be 'user', which is fine.
# (It is necessary if you have a __table_args__, though.)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(35), unique=False)
# and so forth
requested_rels = db.relationship(
'Relationship',
foreign_keys='Relationship.requesting_user_id',
backref='requesting_user'
)
received_rels = db.relationship(
'Relationship',
foreign_keys='Relationship.receiving_user_id',
backref='receiving_user'
)
aspiring_friends = association_proxy('received_rels', 'requesting_user')
desired_friends = association_proxy('requested_rels', 'receiving_user')
def __repr__(self):
# and so forth
class Relationship(db.Model):
# __tablename__ removed, becomes 'relationship'
# __table_args__ removed, see below
requesting_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
receiving_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
# Marking both columns above as primary_key creates a compound primary
# key, which at the same time saves you the effort of defining the
# UNIQUE constraint in __table_args__
status = db.Column(db.Integer)
# Implicit one-to-many relations: requesting_user, receiving_user.
# Normally it would be more convenient to define those relations on
# this side, but since you have two outgoing relationships with the
# same table (User), you chose wisely to define them there.
(Note how I ordered the lines slightly differently and how I used the _id suffix for foreign key columns while reserving the same name without the suffix for the corresponding db.relationships. I would suggest that you adopt this style, too.)
Now you have a clean way to access incoming and outgoing friendship requests as well as the corresponding users directly from your User model. However, this is still less than ideal because you need to write the following code in order to get all confirmed friends of a user:
def get_friends(user):
requested_friends = (
db.session.query(Relationship.receiving_user)
.filter(Relationship.requesting_user == user)
.filter(Relationship.status == CONFIRMED)
)
received_friends = (
db.session.query(Relationship.requesting_user)
.filter(Relationship.receiving_user == user)
.filter(Relationship.status == CONFIRMED)
)
return requested_friends.union(received_friends).all()
(I did not test this; you might need to also join with User in both queries in order for the union to work.)
To make things worse, the model name Relationship as well as the names of several members within the models don't seem to convey very well what they actually mean.
You can improve matters by removing Relationship.status and renaming Relationship to FriendshipRequest. Then, add a second User-to-User association model called Friendship and add a corresponding second set of db.Relationships with backrefs and association_proxys to User. When somebody sends a friendship request, you file a record to FriendshipRequest. If the request is accepted, you remove the record and replace it with a new record in Friendship. This way, instead of using a status code, the status of a friendship is encoded by the table in which you store a pair of users. The Friendship model may look like this:
class Friendship(db.Model):
user1_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
user2_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
# Implicit one-to-many relations: user1, user2
# (defined as backrefs in User.)
(Corresponding db.relationships and association_proxys in User are left as an exercise to the reader.)
This approach saves you half of the filtering operations when you need the confirmed friends of a user. Still, you need to make a union of two queries because your user can be either user1 or user2 in each instance of Friendship. This is inherently difficult because we are dealing with a reflexive symmetric relationship. I think it is possible to invent still more elegant ways to do it, but I think that would be complicated enough to warrant a new question here on Stack Overflow.

django query multiple tables with foreign key pointing on self

lets say I have a model named User, and other models representing actions done by the a user, like "User_clicked", "User_left", "User_ate_cookie" etc. etc.
the User_* models have different fields and inherit from a single abstract class (User_do_something)
my question is this:
what's the django-way of querying ALL the models, from all the relevant tables, that point on a specific user?
eg. I want to do User.get_all_actions() and get a list with mixed type of models in them, containing all the objects that inherit from User_do_something and point on the specific user.
Note: performance is critical. I don't want to make multiple db queries and combine the list, if possible, I want to do it with a single sql select.
some code to be clear:
class User(models.Model):
uuid = models.UUIDField(unique=True, default=uuid.uuid4)
creation_time = models.DateTimeField(auto_now_add=True)
def get_all_actions(self):
'''
return a list with ALL the actions this player did.
'''
??????? how do I do this query ???????
class User_do_action(models.Model):
user = models.ForeignKey(User)
creation_time = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class User_click(User_do_action):
... some fields
class User_left(User_do_action):
... some fields
class User_ate_cookie(User_do_action):
... some fields
etc. etc.
thanks!