I am using flask-sqlalchemy to implement many to many relationship. Tried many options but unable to query all children for a parent.
class Participant(db.Model):
__tablename__='participant'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True)
name = db.Column(db.String(120), unique=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship("User")
#staticmethod
def get_by_user(user):
return Participant.query.filter_by(user=user).first()
def __repr__(self):
return "(Participant '{}')".format(self.email)
competitions = db.relationship("CompetitionParticipant", back_populates="participant")
class Competition(db.Model):
__tablename__='competition'
__table_args__={'extend_existing':True}
id=db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(100),unique=True)
description=db.Column(db.String(255),unique=True)
# type=db.Column(db.String(50))#Team,Individual,Practice,etc.
start_date=db.Column(db.DateTime)
end_date=db.Column(db.DateTime)
result_date=db.Column(db.DateTime)
participants = db.relationship('CompetitionParticipant', back_populates='competition')
def __repr__(self):
return "(Competition '{}')".format(self.name)
class CompetitionParticipant(db.Model):
__tablename__='competition_participant'
__table_args__={'extend_existing':True}
id=db.Column(db.Integer,primary_key=True)
participant_id=db.Column(db.Integer, db.ForeignKey('participant.id'))
competition_id=db.Column(db.Integer, db.ForeignKey('competition.id'))
competition=db.relationship("Competition",back_populates="participants")
participant=db.relationship("Participant",back_populates="competitions")
def __repr__(self):
return "(competition_id, participant_id '{},{}')".format(self.competition_id,self.participant_id)
In this many to many relationship example how will I query all the competitions a participant is enrolled in?
I have tried following:
participant.competitions # returns relationship object
everything else I tried was syntax error. What is right ORM way to extract all competitions a participant is enrolled in?
If you, for example, want to get competitions data for a specific user:
participants = Participant.query.filter_by(id=1).all()
you can iterate through participants and get data :
for participant in participants:
for competition in participant.competitions:
print(competition.competition.name)
Shouldn't you be able to do something like this if you wanted to find out about the participant with id=1:
participant = Participant.query.get(1)
for item in participant.competitions:
print(item.name)
Related
The system is mutli user(based on signals) :
Company
Driver
Client
Company and Driver have team and can invite drivers to their teams
So company and driver share the team field which is many to many field
I found two solutions :
First one:
Team field will be in the BaseUser mode.
pros:
Simple implementation
Cons:
client user will have a team field on it’s record in database which mean redundant data.
Second one:
Create abstract model hold team field, then both company, driver models will inherit that abstract model.
Pros:
Avoid creating team field in Client user
Cons:
More code
Code a little bit messy
Increase complexity of invitation and join logic ( must be optimized to fetch if the invitation creator is a company or driver then deal depend on that) , (in first solution the creator always will be the BaseUser)
Add an extra fields to invitation object refere to CompanyMembership and DriverMembership, (in first solution was just a field refere to the general membership), but this invitation object will delete after joining , will be kept in database in case the invited one does not joined , and can solve by using celery to delete expired invitations.
is there another better solution and if not which one of both is better ?
The second solution implementation:
Abstract models that hold the common fields:
class CommonFields(models.Model):
team = models.ManyToManyField(
"driver.DriverProfile", symmetrical=False, blank=True, through="Membership")
user = models.OneToOneField(
"users.User", on_delete=models.CASCADE, related_name="driver_profile")
class Meta:
abstract = True
class BaseMembership(models.Model):
class MemberType(models.TextChoices):
FAMILY = "FAMILY", _("Family")
TAXI_DRIVER = "TAXI DRIVER", _("Taxi Driver")
member = models.ForeignKey(
"driver.DriverProfile", on_delete=models.CASCADE, null=True)
date_joined = models.DateTimeField(default=now)
member_type = models.CharField(
_("Type"), max_length=50, choices=MemberType.choices, default=MemberType.TAXI_DRIVER
)
class Meta:
abstract = True
default_related_name = 'team_members'
def save(self, *args, **kwargs):
# Only allow this relationship to be created if
if self.leader != self.member:
super(BaseMembership, self).save(*args, **kwargs)
Company Membership:
# Company Memebership ===========================================================================
class Membership(BaseMembership):
leader = models.ForeignKey(
CompanyProfile, on_delete=models.CASCADE, related_name='owner')
class Meta:
default_related_name = 'company_members'
Driver Membership
# Driver Membership ================================================================================
class Membership(BaseMembership):
leader = models.ForeignKey(
DriverProfile, on_delete=models.CASCADE, related_name='owner')
Invitation and joining view(I have not update it yet to deal with the second solution):
class InvitationView(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
#action(["post"], detail=False)
def invite(self, request, format=None):
serializer = InvitationSerializer(
data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)
invitation = serializer.save()
uid = invitation.uid
data = {"uid": uid}
return Response(data, status=status.HTTP_201_CREATED)
#action(["post"], detail=False)
def join(self, request, format=None):
serializer = JoinSerializer(
data=request.data)
serializer.is_valid(raise_exception=True)
uid = serializer.uid
try:
invitation = Invitation.objects.get(uid=uid)
except (Invitation.DoesNotExist):
return Response(
"invalid invitation code", status=status.HTTP_400_BAD_REQUEST
)
membership = invitation.membership
invitation.delete()
driver = request.user.driver_profile
if driver != membership.leader:
membership.driver = driver
membership.save()
return Response("joined successfully", status=status.HTTP_201_CREATED)
else:
membership.delete()
return Response("leader can't join as a driver to his team", status=status.HTTP_400_BAD_REQUEST)
I would setup my tables like that:
Company
companyid, PK
companyname, NN
Team
teamid, PK
teamname, NN
Company-Team
companyid, FK, Company.companyid, NN
teamid, FK, Team.teamid, NN
Driver
driverid, PK
drivername, NN
Driver-Team
driverid, FK, Driver.driverid, NN
teamid, FK, Team.teamid, NN
Client
clientid, PK
clientname, NN
PK: primary key
FK: foreign key
NN, not null
Many fields can be added to each table, but this structure respects your many-to-many relations, and allows you greater flexibility for your SQL queries, and future maintenance.
For Client, you did not specify what relations apply to them. But a driver and a client are different objects. Unless of course you want to use another level of abstraction and have a users table with types driver and client. Unless the same person can be a client and a driver at the same time, I would not bother adding this complexity.
I am building an admin dashboard for my web app using Flask-Admin. For the user/address relationship, I am using a one to one relationship. On the user edit form, I'd like to be able to edit the individual components of the address (i.e. street address, city or zip) similar to what inline_models provides. Instead, flask-admin generates a select field and only allows me to select a different addresses.
I tried using inline_models = ['address'] in the UserModelView definition. However, I got the address object not iterable error due to the user/address relationship being configured to uselist=False. Switching uselist to True would affect other parts of my code, so I'd prefer to leave it as False.
From looking in flask-admin/contrib/sqla/forms, within the function get_forms, its being assigned a one to many tag which is what drives the use of a select field.
Before diving in further, I figured it best to see if anyone else has come across this or has a recommended fix/workaround.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64))
address = db.relationship("Address", backref="user",
cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
line1 = db.Column(db.String(128))
zip = db.Column(db.String(20), index=True)
city = db.Column(db.String(64), index=True, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id",
ondelete="CASCADE"))
admin.py
class UserModelView(ModelView):
column_list = [User.username, 'address']
form_columns = (User.username, 'address')
admin = Admin(name='Ask', template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))
You can create 2 relations
# Relation for flask admin inline model
address_cms_relationsip = db.relationship(
"Address", backref="user", cascade="all, delete-orphan", lazy=False,
uselist=True, passive_deletes=True)
address_relationship = db.relationship(
"Address", cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
#property
def address(self):
return self.address_relationship
In your code you can use property address
user: User # some User object
user.address.city
Why target_dialogue is always None?
Model:
class Dialogue(models.Model):
name = models.CharField(max_length=30, blank=True)
is_conference = models.BooleanField(default=False)
participants = models.ManyToManyField(
Person,
related_name='dialogues',
)
def __str__(self):
return self.name or str(self.pk)
And in view I want to get suitable dialogue which contain in participants field 2 objects - user and companion. And if this dialogue doesn't exist I create it:
target_dialogue = None
try:
target_dialogue = Dialogue.objects.get(is_conference=False,participants__in=[user, companion])
except ObjectDoesNotExist:
target_dialogue = Dialogue()
target_dialogue.save()
target_dialogue.participants.add(user)
target_dialogue.participants.add(companion)
finally:
return render(request, 'dialogues/dialogue.html', {
'dialogue': target_dialogue,
})
But target_dialogue is always None. What's a reason of it? I was supposed to solve only a trouble in getting a dialogue from db in order to bad filter parameters, but now I have doubts about it. Maybe something else?
request.user is not a object of Person model with which you have the relation in Dialogue.
You have to first fetch the person object:
user = Person.objecs.get(user=request.user). # According to your person model
Follow same for companion and then query:
target_dialogues = Dialogue.objects.filter(is_conference=False,participants__in=[user,companion]
I'm been trying to create an app that allows users to follow each other profile since yesterday and today and I haven't been successful so far.
I'm having trouble creating a following function that allows me to retrieve users from a particular user he follows.
Example . If John follows Diana . I want to able to retrieve the user called Diana and use it with my modules.
I'm really sorry if this doesn't make sense . I'm trying my hardest to explain my situation.
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
class Board(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
Most of these solutions gave me no query
This was one of the solutions I tried.
class UserLink(models.Model):
from_user = models.ForeignKey(User , related_name = "following_set")
to_user = models.ForeignKey(User , related_name = "follower_set")
date_added = models.DateTimeField(default = datetime.now)
def __unicode__(self):
return "%s is following %s" % (self.from_user.username,self.to_user.username)
def save(self,**kwargs):
if self.from_user == self.to_user:
raise ValueError("Cannot follow yourself ")
super(UserLink , self).save(**kwargs)
class Meta:
unique_together = (('to_user','from_user'),)
I tried to retrieve the users that a particular user followed and use it against my modules such as Person but it gave me an error No query exist.
def Follow(request,username=""):
if request.method == "POST":
username = request.POST.get('follow',False)
user = User.objects.get(username=username)
UserLink.objects.create(from_user=request.user,to_user=user)
return HttpResponseRedirect(reverse('world:Profile'))
return HttpResponseRedirect(reverse('world:Profile'))
I also tried this following function but it only followed himself and I changed self to User but it didn't allow me to put the person to follow
class UserProfile(models.Model):
user = models.OneToOneField(User)
follows = models.ManyToManyField('self', related_name='followed_by', symmetrical=False)
>>>from pet.models import *
>>>from django.contrib.auth.models import User
>>>user = User.objects.get(username='Peter')
>>>user1 = User.objects.get(username='Sarah')
>>>p = UserProfile.objects.filter(user=user,follows=user1)
>>>Error no field called follows
How can I create a following class that allows retrieve the people that they followed and use it with my modules such as Person?
Can someone help me . Thannk you community!
If I understand correctly, youu are on the right track with the many to many relationship. What you need is to modify your existing Person class to include this information.
Since information about who someone follows or is following is essentially information about that person and so you shouldn't really need to define a new class to implement that functionality.
I would suggest modifying your Person like so.
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
following = models.ManyToManyField('self', related_name='followers', symmetrical=False, blank=True, null=True)
What this line does is makes a many to many relationship between the class Person and its self.
Many to many relationships work a little different to other relationships and I suggest you read the Django documentation https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/.
But you should now be able to setup and access the relationship like this.
>>>john = Person.objects.get(name="John")
>>>diana = Person.objects.get(name="Diana")
>>>john.following.add(diana)//setup the many to many relationship
>>>john.save()
>>>john.following.all()
//This should return a queryset of Person objects which john is following.
//eg Diana
>>>diana.followers.all()
//This should return a queryset of Person objects which are following Diana.
//eg. John.
Easy, how awesome is Django!
I have successfully made django-haystack with elasticsearch to work.
In the example below, I can search for any sales item and it would show up in the results.
I have created an Index:
class SalesItemIndex(SearchIndex, Indexable):
text = CharField(document=True, use_template=True)
item_description = CharField(model_attr='item_description')
def get_model(self):
return SalesItem
def index_queryset(self):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
The model SalesItem:
class SalesItem(models.Model):
item_description = models.CharField(_(u"Item Description"), max_length=40)
company = models.ForeignKey(Company)
def __unicode__(self):
return self.item_description
The problem is though, the user can search for all sales items, even those that don't belong to his company.
Usually instead of returning all salesitems = SalesItem.objects.all() I would rather use this to make sure the user only sees the items that belons to his company:
profile = request.user.get_profile()
sales_items = profile.company.salesitem_set.all()
Would I be able to limit my search with this rule?
You could index the company id in the SalesItemIndex as well:
class SalesItemIndex(SearchIndex, Indexable):
...
company = IntegerField(model_attr='company_id')
And filter it directly in SearchQuerySet:
sales_items = salesitem_set.filter(company=profile.company_id)