Flask SQLAlchemy 2 ForeignKey columns going back to the same table - database

I have a Users table and a Tasks table. The Tasks table has an "author" column and an "assignee" column, both of which are foreign keys to the unique ID in Users.
As you might expect, this produced an error: "Could not determine join condition between parent/child tables on relationship Users.created_tasks - there are multiple foreign key paths linking the tables."
What's another way to record both the "author" and "assignee" if I wanted to have both columns in the table?
class Tasks(db.Model):
// ...
author = db.Column(db.Integer, db.ForeignKey('users.id'))
assignee = db.Column(db.Integer, db.ForeignKey('users.id'))
class Users(db.Model, UserMixin):
// ...
created_tasks = db.relationship('Tasks', backref = 'ctasks_user')
assigned_tasks = db.relationship('Tasks', backref = 'atasks_user')

You can specify the primaryjoin attribute of each relationship to differentiate the two:
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
connection_uri = (
"mssql+pyodbc://#localhost:49242/myDb?driver=ODBC+Driver+17+for+SQL+Server"
)
engine = sa.create_engine(connection_uri)
with engine.begin() as conn:
conn.execute(sa.text("DROP TABLE IF EXISTS task_t"))
conn.execute(sa.text("DROP TABLE IF EXISTS user_t"))
Base = declarative_base()
class Task(Base):
__tablename__ = "task_t"
id = sa.Column(sa.Integer, primary_key=True)
description = sa.Column(sa.String(50))
author = sa.Column(sa.Integer, sa.ForeignKey("user_t.id"))
assignee = sa.Column(sa.Integer, sa.ForeignKey("user_t.id"))
def __repr__(self):
return f"<Task(id={self.id}, description='{self.description}')>"
class User(Base):
__tablename__ = "user_t"
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String(50))
created_tasks = relationship(
Task,
primaryjoin="User.id == Task.author",
backref="user_created_tasks",
)
assigned_tasks = relationship(
Task,
primaryjoin="User.id == Task.assignee",
backref="user_assigned_tasks",
)
def __repr__(self):
return f"<User(id={self.id}, name='{self.name}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
homer = User(name="Homer")
bart = User(name="Bart")
lisa = User(name="Lisa")
session.add_all([homer, bart, lisa])
session.commit()
mow_the_lawn = Task(
description="Mow the lawn", author=homer.id, assignee=bart.id
)
wash_the_car = Task(
description="Wash the car", author=homer.id, assignee=lisa.id
)
session.add_all([mow_the_lawn, wash_the_car])
session.commit()
with engine.begin() as conn:
result = conn.execute(
sa.text("SELECT * FROM user_t ORDER BY id")
).fetchall()
print(result)
# [(1, 'Homer'), (2, 'Bart'), (3, 'Lisa')]
result = conn.execute(
sa.text("SELECT * FROM task_t ORDER BY id")
).fetchall()
print(result)
# [(1, 'Mow the lawn', 1, 2), (2, 'Wash the car', 1, 3)]
print(homer.created_tasks)
# [ <Task(id=1, description='Mow the lawn')>, <Task(id=2, description='Wash the car')>]
print(bart.assigned_tasks)
# [ <Task(id=1, description='Mow the lawn')>]

Related

How to perform query on one to many relations tables using ORM and define serializers for it?

data in request
city_name = 'Nashik'
tags = ['tag 1','tag 2', 'tag 3']
I try this
ws = WorkSamplesModel.objects.filter(business_account__serviceareasmodel__city_name=city_name,
business_account__professiontagsmodel__tag_name__in=tags_list,
is_business_card_image=True).distinct()
Now I want business_title ,business_description,status,note from BusinessAccountModel and
work_sample_image, work_sample_description, is_business_card_image from WorkSampleModel and
user_name,profile_pic from UserModel
how to create serializer for it?
models
I want to find a specific BusinessAccountModel using city_name and tags
and then one WorkSamplesModel for each BusinessAccountModel.
class BusinessAccountModel(models.Model):
business_title = models.CharField(max_length=70)
business_description = models.CharField(max_length=500)
status = models.CharField(max_length=100)
note = models.CharField(max_length=200)
user = models.OneToOneField(UserModel,on_delete=models.CASCADE)
class Meta:
db_table = 'wp_business_acc'
class ProfessionTagsModel(models.Model):
tag_name = models.CharField(max_length=20)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'profession_tags'
class ServiceAreasModel(models.Model):
city_name = models.CharField(max_length=20)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'service_areas'
class WorkSamplesModel(models.Model):
work_sample_image = models.ImageField(blank=True,null=True,upload_to="work_samples")
work_sample_description = models.CharField(max_length=1000)
is_business_card_image = models.BooleanField(default=False)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'work_samples'
BusinessAccountModel - ProfessionTagsModel : 1:M
BusinessAccountModel - ServiceAreasModel : 1:M
BusinessAccountModel - WorkSamplesModel : 1:M
ws = WorkSamplesModel.objects.filter(business_account__serviceareasmodel__city_name=city_name,
business_account__professiontagsmodel__tag_name__in=tags_list,
is_business_card_image=True).distinct()
Serializer for it
class SearchWorkingProgessionalsSerializers(serializers.Serializer):
business_title = serializers.CharField(source='business_account.business_title')
business_description = serializers.CharField(source='business_account.business_description')
status = serializers.CharField(source='business_account.status')
note = serializers.CharField(source='business_account.note')
work_sample_image = serializers.ImageField()
work_sample_description = serializers.CharField(max_length=1000)

django.db.utils.IntegrityError(could not create unique index)

When am trying to add new Unique_id (uuid) field in the existing django models , It returns the intgerity error.
models.py:
class Directory(models.Model):
unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=120)
path = models.CharField(max_length=240)
Views.py:
def get_dir_dict(request, dir):
dir_info['pk'] = query.pk
dir_info['unique_id'] = query.unique_id
dir_info['name'] = query.name
dir_info['path'] = query.path
error:
File "/home/sitharth/zeal/lib64/python3.6/site-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: could not create unique index "zkloud_directory_unique_id_key"
DETAIL: Key (unique_id)=(734e8926-386b-47a9-9ac0-d617f45f7113) is duplicated.

How to create data to the database using views

I am trying to create order amount from the catalogue which works like a shopping cart but the amount returned is 1 for all orders made:
views.py
def get_user_pending_order(request):
#get order from correct profile
user_profile = get_object_or_404(Profile,user=request.user)
order = Order.objects.filter(owner=user_profile,is_ordered=True)
if order.exists():
#to get an order in the list of filtered orders
return order[0]
return 0
def add_to_catalogue(request,employee_id):#product_id,employee_id
user_profile= get_object_or_404(Profile, user =request.user)
order_to_purchase = get_user_pending_order(request)
amount= self.order_to_purchase.get_catalogue_total(),
employee = Employee.objects.get(pk=employee_id)
if employee in request.user.profile.ebooks.all():
messages.info(request,'you already own this ebook')
return redirect(reverse('freelance:employee_list'))
order_task,status =
OrderTask.objects.get_or_create(employee=employee)
user_order,status = Order.objects.get_or_create(owner=user_profile,
is_ordered=False,order_amount=amount)####TThis IS WHWERE TO EDIT TO PREVENT
RE ORDERNG OF FREELANCING
user_order.tasks.add(order_task)
if status:
user_order.ref_code = generate_order_id()
user_order.save()
messages.info(request,"task added to catalogue")
return redirect(reverse('freelance:employee_list'))
def get_user_pending_order(request):
#get order from correct profile
user_profile = get_object_or_404(Profile,user=request.user)
order = Order.objects.filter(owner=user_profile,is_ordered=True)
if order.exists():
#to get an order in the list of filtered orders
return order[0]
return 0
models.py
class Order(models.Model):
ref_code = models.CharField(max_length=15)
owner = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=
True)
is_ordered = models.BooleanField(default=False)
tasks = models.ManyToManyField(OrderTask)
date_ordered = models.DateTimeField(auto_now= True)
order_amount = models.DecimalField(default=0.01, max_digits= 10,
decimal_places=2)
def order_tasks(self):
return ','.join([str(c.employee) for c in self.tasks.all()])
def get_catalogue_tasks(self):
return self.tasks.all()
def get_catalogue_total(self):
return sum([task.employee.pricing for task in self.tasks.all()])
def __str__(self):
return '{0} - {1}'.format(self.owner, self.ref_code, self.order_amount)
def tasks_summary(request):
existing_order = get_user_pending_order(request)
my_user_profile = Profile.objects.filter(user=
request.user).first()
my_orders = Order.objects.filter(is_ordered= True, owner=
my_user_profile)
order_to_purchase = get_user_pending_order(request)
amount= order_to_purchase.get_catalogue_total(),
order = Order.objects.filter(is_ordered= True)
context = {
'my_orders':my_orders,
'order':order,
'amount':amount
# 'total':total,
}
return render(request,
'freelance/tasks_summary.html',context)###Belongs to the admin sisde
Output of the template
I am getting this error when I try to add anything to the catalogue:
AttributeError at /admin/tasks_summary/
'int' object has no attribute 'get_catalogue_total'

ValueError: variable needs to have a value for field "id" before this many to many relationship can be used - Django

I created a datamodel in Django, and now I created a script to auto populate the models using web-scraped values. However when I run the script I get the following error:
ValueError: variable needs to have a value for field "id" before this many to many relationship can be used
Models.py
class Books(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Meta:
ordering = ['-title']
class Author(models.Model):
book = models.ManyToManyField(Books)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=200)
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
class Meta:
ordering = ['last_name','first_name']
class Book_details(models.Model):
book = models.ForeignKey(Books,
on_delete=models.CASCADE,
null=True) # models.SET_NULL weggehaald
pages = models.CharField(max_length=250)
publ_year = models.CharField(max_length=250)
edition = models.CharField(max_length=30) # paperback, hardcover, audiobook, etc
def __str__(self):
return "{} - pages: <{}>, edition: <{}>".format(self.book.title,
self.pages,
self.edition)#
class Cover(models.Model):
book = models.OneToOneField(Books,
on_delete=models.CASCADE)
path = models.CharField(max_length=500)
def __str__(self):
return "<Cover <path={}>".format(self.id, self.path)
populate_script
def add_book(title):
b = Books.objects.get_or_create(title = title)[0]
print(b)
b.save()
return b
def populate(scraped_tuple):
fake = Faker()
for _ in range(len(scraped_tuple)):
b_title = scraped_tuple[_][0][0]
new_book = add_book(b_title)
b_author_first = scraped_tuple[_][0][1].split(" ")[0]
b_author_last = scraped_tuple[_][0][1].split(" ")[1]
b_pages = scraped_tuple[_][0][2].split(" ")[0]
b_publ_year = fake.year()
b_edition = scraped_tuple[_][0][3].split(",")[0]
b_cover = scraped_tuple[_][0][4]
new_details = Book_details.objects.get_or_create(book = new_book, pages = b_pages, publ_year = b_publ_year, edition = b_edition)[0]
new_author = Author.objects.get_or_create(book = new_book, first_name = b_author_first, last_name = b_author_last)[0]
new_cover = Cover.objects.get_or_create(book = new_book, path = b_cover)[0]
The scraped_tuple is a return value from the webscraper containing the details.
(Part of) the Traceback:
Books.models.DoesNotExist: Author matching query does not exist.
File "C:\path\to\LibraryApp\Library_WebA
pp\Library\populate.py", line 45, in populate
new_author = Author.objects.get_or_create(book = new_book, first_name = b_author_first, last_nam
e = b_author_last)[0]
Followed by:
ValueError: "<Author: Mary McCarthy>" needs to have a value for field "id" before this many-to-many relationship can be used.
So, it seems that something goes awfully wrong when trying to execute the new_author statement, because of the many-to-many field "book" in the Author model. How can I resolve this. Do I need a similar function for an Author object like I have for the Book in add_book()?
It seems the new_details statement executes just fine (title and book_details appear correctly in the database in the admin part of Django).
As mentioned in the docs, user .add() to associate the records in many to many field.
def populate(scraped_tuple):
fake = Faker()
for _ in range(len(scraped_tuple)):
b_title = scraped_tuple[_][0][0]
new_book = add_book(b_title)
b_author_first = scraped_tuple[_][0][1].split(" ")[0]
b_author_last = scraped_tuple[_][0][1].split(" ")[1]
b_pages = scraped_tuple[_][0][2].split(" ")[0]
b_publ_year = fake.year()
b_edition = scraped_tuple[_][0][3].split(",")[0]
b_cover = scraped_tuple[_][0][4]
new_details = Book_details.objects.get_or_create(book = new_book, pages = b_pages, publ_year = b_publ_year, edition = b_edition)[0]
new_author = Author.objects.get_or_create(first_name = b_author_first, last_name = b_author_last)[0]
# add many to many fields this way:
new_author.book.add(new_book)
new_cover = Cover.objects.get_or_create(book = new_book, path = b_cover)[0]

Flask model return all table columns

i have this on my model
class Social(db.Model):
__tablename__ = 'social_auth_usersocialauth'
id = db.Column('id',db.Integer, primary_key=True)
provider = db.Column('provider',db.String(32))
extra_data = db.Column('extra_data',db.String())
uid = db.Column('uid',db.String(255))
def __init__(self,id, provider, extra_data, uid):
self.id = id
self.provider = provider
self.extra_data = extra_data
self.uid = uid
def __repr__(self):
return self.uid
and when i call it to my view, i just get the uid, yes i know because in my model i just returned it's uid, the question is, how can i return all of it's table's columns ?
like id, provider, also extra_data column,,,
Thank you.
The simpler way is to comment your repr function and return the Social object and use it's attributes by calling
social = Social.query.first() # example
id = social.id
providor = object.providor
...
But if you really want to do it this way you can return all attributes in repr by:
def __repr__(self):
return self.id, self.providor, self.extra_data, self.uid
and changing this in you views to this and get all values by::
id, providor, extra_data, uid = Social.query.first() # example

Resources