I am new to FastAPI and SQL-Alchemy, I am having trouble creating a schema and relationship of the many-to-many table (association table).
Here is an example, a student can enroll in multiple courses and a single course can have multiple students thus making many-to-many relationship.
Database Models
class Student(Base):
__tablename__ = "student"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
class Course(Base):
__tablename__ = "course"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
class StudentCourse(Base):
__tablename__ = "student_course"
id = Column(Integer, primary_key=True, index=True)
student_id = Column(Integer, ForeignKey("student.id"), nullable=False)
course_id = Column(Integer, ForeignKey("course.id"), nullable=False)
What relation do I need to define and how to design a schema from which I can get the following responses:
Response 1
[
{
"id": 1,
"name": "Student A",
"courses": [
{
"id": 1,
"name": "Course A"
},
{
"id": 2,
"name": "Course B"
}
]
},
{
"id": 2,
"name": "Student B",
"courses": [
{
"id": 1,
"name": "Course A"
},
{
"id": 3,
"name": "Course C"
}
]
},
{...}
]
Response 2
[
{
"id": 1,
"name": "Course A",
"students": [
{
"id": 1,
"name": "Student A"
},
{
"id": 2,
"name": "Student B"
}
]
},
{
"id": 2,
"name": "Course B",
"students": [
{
"id": 1,
"name": "Student A"
},
]
},
{
"id": 3,
"name": "Course C",
"students": [
{
"id": 2,
"name": "Student B"
},
]
},
{...}
]
In my opinion, it is simpler and preferable to use an association table instead of an association object for this many-to-many relationship, since you don't need extra fields.
So, according to the documentation, your database models should be:
student_course = Table("student_course", Base.metadata,
Column("student_id", ForeignKey("student.id"), primary_key=True),
Column("course_id", ForeignKey("course.id"), primary_key=True))
class Student(Base):
__tablename__ = "student"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
courses = relationship("Course",
secondary=student_course,
back_populates="students")
class Course(Base):
__tablename__ = "course"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
students = relationship("Student",
secondary=student_course,
back_populates="courses")
and your pydantic models (schemas):
class Student(BaseModel):
id: int
name: str
class Config:
orm_mode = True
class Course(BaseModel):
id: int
name: str
class Config:
orm_mode = True
class StudentOut(Student):
courses: List[Course]
class CourseOut(Course):
students: List[Student]
where StudentOut and CourseOut are your response models
Related
I have the following code in my app:
models.py
class MainTable(Base):
__tablename__ = "main_table"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True, nullable=False)
owner = Column(String, index=True)
metas = relationship("ChildTable", back_populates="item")
class ChildTable(Base):
__tablename__ = "child"
id = Column(Integer, primary_key=True, index=True)
segment = Column(Integer)
name = Column(String)
main_name = Column(String, ForeignKey('main_table.name'), nullable=False)
item = relationship("MainTable", back_populates="metas")
__table_args__ = (UniqueConstraint('segment', 'main_name', name='_meta_for_main'),
)
And when I run my app, and create new obj in db:
{
"name": "string",
"owner": "string",
"metas": [
{
"segment": 0,
"name": "string",
"main_name": "string",
}
]
}
I get the following error:
AttributeError: 'MainMetaCreate' object has no attribute '_sa_instance_state'
schemas.py
class EnvironmentMetaBase(BaseModel):
segment: int
name: str
main_name: str
Haw you met the following problem?
models.py
class Employees(models.Model):
emp_no = models.IntegerField(primary_key=True)
birth_date = models.DateField()
first_name = models.CharField(max_length=14)
last_name = models.CharField(max_length=16)
gender = models.CharField(max_length=1)
hire_date = models.DateField()
class Meta:
managed = False
db_table = 'employees'
class Salaries(models.Model):
emp_no = models.ForeignKey(Employees, models.DO_NOTHING, db_column='emp_no',related_name='salaries', primary_key=True)
salary = models.IntegerField()
from_date = models.DateField()
to_date = models.DateField()
class Meta:
managed = False
db_table = 'salaries'
unique_together = (('emp_no', 'from_date'),)
Mysql raw query:
SELECT `employees`.`emp_no`, `employees`.`birth_date`, `employees`.`first_name`, `employees`.`last_name`, `employees`.`gender`, `employees`.`hire_date`,
(
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'salary', `salaries`.`salary`,
'from_date', `salaries`.`from_date`,
'to_date', `salaries`.`to_date`
)
) FROM `salaries` WHERE (`employees`.`emp_no` = `salaries`.`emp_no`)
) as salaries
FROM `employees`
INNER JOIN `salaries` root_salaries ON `root_salaries`.`salary` > 60000
WHERE `employees`.`emp_no` = `root_salaries`.`emp_no` LIMIT 100 OFFSET 100
Output for raw query:
[
{
"emp_no": 10017,
"birth_date": "1958-07-06",
"first_name": "Cristinel",
"last_name": "Bouloucos",
"gender": "F",
"hire_date": "1993-08-03",
"salaries": [
{
"salary": 71380,
"to_date": "1994-08-03",
"from_date": "1993-08-03"
},
{
"salary": 75538,
"to_date": "1995-08-03",
"from_date": "1994-08-03"
}
]
},
{
"emp_no": 10018,
"birth_date": "1954-06-19",
"first_name": "Kazuhide",
"last_name": "Peha",
"gender": "F",
"hire_date": "1987-04-03",
"salaries": [
{
"salary": 55881,
"to_date": "1988-04-02",
"from_date": "1987-04-03"
},
{
"salary": 59206,
"to_date": "1989-04-02",
"from_date": "1988-04-02"
},
{
"salary": 61361,
"to_date": "1990-04-02",
"from_date": "1989-04-02"
}
]
}
]
My question is how to generate the above raw query using Django ORM.
Thanks.
Views.py
query_set = Employees.objects.filter(salaries__salary__gt=60000).annotate(
salaries_1=Subquery(
Salaries.objects.filter(emp_no=OuterRef('emp_no'), salary__gt=60000).values(
salaries=JSONArrayAgg(JSONObject(
salary=F('salary'),
from_date=F('from_date'),
to_date=F('to_date')
))))
).distinct()[100:200]
employee_ser = EmployeesWithSalaryGroupSerializer(query_set, many=True)
data = employee_ser.data
Serializers.py
class EmployeesWithSalaryGroupSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField()
last_name = serializers.CharField()
gender = serializers.CharField()
hire_date = serializers.DateField()
salaries_1 = serializers.JSONField()
def to_representation(self, instance):
ret = super(EmployeesWithSalaryGroupSerializer, self).to_representation(instance=instance)
ret['salaries'] = json.loads(instance.salaries_1)
ret.pop('salaries_1')
return ret
I have two models Employee and AccessDr.
Employee Model=>
class Employee(models.Model):
empid = models.CharField(max_length=20, unique=True)
empname = models.CharField(max_length=50)
phone = models.CharField(max_length=20, blank=True)
token = models.ForeignKey(
Company, to_field='token', on_delete=models.CASCADE)
def __str__(self):
return "%s" % (self.empid)
class Meta:
managed = True
db_table = 'employee'
AccessDr Model=>
class AccessDr(models.Model):
empid = models.ForeignKey(
Employee, to_field='empid', on_delete=models.CASCADE)
_date = models.DateField()
_time = models.IntegerField()
device = models.CharField(max_length=5)
takey = models.CharField(max_length=3, default='00')
token = models.ForeignKey(
Company, to_field='token', on_delete=models.CASCADE)
def __str__(self):
return "%s %s" % (self.empid, self._date)
class Meta:
ordering = ['_date']
managed = True
db_table = 'tstrdoor'
I would like to return object when the request to AccessDr like SQL left join, Example json=>
{
empid:'',
empname:'', <=this one from employee model
phone:'', <=this one from employee model
_date:'',
_time:'',
.
.
}
How can I achieve that one?
have a look at this LEFT JOIN Django ORM
read this as well https://docs.djangoproject.com/en/2.2/topics/db/queries/#spanning-multi-valued-relationships
you can print query to look at how it translates to SQL by
Employes=AccessDr.objects.values('employee__empname','employee__phone')
print(Employes.query)
Are you using serializers in the views? in this case, you could create a serializer field
which will put agreement data as an array like this
class CustomerSerializer(serializers.ModelSerializer):
agreements = AgreementSerializer(many=True, read_only=True)
class Meta:
model = Customer
fields = [
'id',
'username',
'mail',
'fName',
'lName',
'fNameEng',
'lNameEng',
'personalId',
'phone',
'crmId', # "ID": "20995",
'agreements',
]
depth = 1
Are you using serializers in the views? in this case, you could create serializer field
which will put agreement data as an array like this
{
"id": 985,
"username": null,
"mail": "undefined",
"fName": "Merab",
"lName": "Dasv",
"fNameEng": "Merab",
"lNameEng": "Dasv",
"personalId": "01022342346003629",
"phone": "5912324234282331",
"crmId": 1439,
"agreements": [
{
"id": 884,
"signDate": "2015-04-16",
"accountDate": "2015-05-01",
"amount": 0,
"comBalance": -1445.0,
"status": 1,
"details": [
{
"square": 32.38,
"amount": 35.0,
"object": {
"id": 578,
"object": 2,
"block": 1,
"floor": 19,
"flat": "7",
"cadastre": "05.24.04.055.01.563",
"square": 32.38,
"pCounter": 25915123146,
"wCounter": 104412312435,
"accountDate": "2015-04-01T00:00:00",
"comBalance": -1445.0,
"comDeptAmount": 1895.0,
"rentDate": null,
"active": 1,
"finaAmount": 0,
"crmId": 0
}
},
]
},
]
}
I have struggling in this error unknown column error.
I have 3 table the following are
Marketplace --- One to Many relationship (marketplace has many products.)
Product --- One to One relationship (Product belongs to marketplace)
Country -- One to many relationship (Country has many products.)
The sample JSON given below :
[{
"id": 1,
"name": "XYZ Name",
"code": "XYZCODE",
"Products": [
{
"id": 150,
"product_name": "ABC",
"product_location": 19,
"state_id": 24,
"city": "California",
"Country": {
"id": 19,
"name": "USA"
}
},
{
"id": 154,
"product_name": "DEF",
"product_location": 19,
"state_id": 24,
"city": "New York",
"Country": {
"id": 19,
"name": "USA"
}
}
]
}]
If am query city and Country name inside the Country table getting following error in sequelize
{
"code": "ER_BAD_FIELD_ERROR",
"errno": 1054,
"sqlState": "42S22",
"sqlMessage": "Unknown column 'Products->Country.name' in 'on clause'",
"sql": "SELECT `MarketplaceType`.`id`, `MarketplaceType`.`name`, `MarketplaceType`.`code`, `Products`.`id` AS `Products.id`, `Products`.`product_name` AS `Products.product_name`, `Products`.`product_location` AS `Products.product_location`, `Products`.`state_id` AS `Products.state_id`, `Products`.`city` AS `Products.city`, `Products->Country`.`id` AS `Products.Country.id`, `Products->Country`.`name` AS `Products.Country.name` FROM `marketplace_type` AS `MarketplaceType` LEFT OUTER JOIN `product` AS `Products` ON `MarketplaceType`.`id` = `Products`.`marketplace_type_id` AND `Products`.`status` = 1 AND `Products`.`marketplace_id` = 1 AND (`Products`.`city` = 'California' OR `Products->Country`.`name` = 'California') LEFT OUTER JOIN `country` AS `Products->Country` ON `Products`.`product_location` = `Products->Country`.`id` WHERE `MarketplaceType`.`status` = 1 AND `MarketplaceType`.`marketplace_id` = 1;"
}
AND MYSQL Query are following :
SELECT `MarketplaceType`.`id`, `MarketplaceType`.`name`, `MarketplaceType`.`code`, `Products`.`id` AS `Products.id`, `Products`.`product_name` AS `Products.product_name`, `Products`.`product_location` AS `Products.product_location`, `Products`.`state_id` AS `Products.state_id`, `Products`.`city` AS `Products.city`, `Products->Country`.`id` AS `Products.Country.id`, `Products->Country`.`name` AS `Products.Country.name` FROM `marketplace_type` AS `MarketplaceType` LEFT OUTER JOIN `product` AS `Products` ON `MarketplaceType`.`id` = `Products`.`marketplace_type_id` AND `Products`.`status` = 1 AND `Products`.`marketplace_id` = 1 AND (`Products`.`city` = 'California' OR `Products->Country`.`name` = 'California') LEFT OUTER JOIN `country` AS `Products->Country` ON `Products`.`product_location` = `Products->Country`.`id` WHERE `MarketplaceType`.`status` = 1 AND `MarketplaceType`.`marketplace_id` = 1;
ORM Query using sequelize :
var marketplaceTypeQueryObj = {};
var productCountQueryParames = {};
marketplaceTypeQueryObj['status'] = status["ACTIVE"];
marketplaceTypeQueryObj['marketplace_id'] = marketplace['WHOLESALE'];
productCountQueryParames['status'] = status["ACTIVE"];
productCountQueryParames['marketplace_id'] = marketplace['WHOLESALE'];
if (req.query.origin) {
productCountQueryParames['$or'] = [{
city: req.query.origin
}, {
'$Products.Country.name$': req.query.origin
}, {
'$Products.State.name$': req.query.origin
}];
}
console.log('productCountQueryParames', productCountQueryParames);
model['MarketplaceType'].findAll({
where: marketplaceTypeQueryObj,
include: [{
model: model['Product'],
where: productCountQueryParames,
include: [{
model: model['Country'],
attributes: ['id', 'name']
}, {
model: model['State'],
attributes: ['id', 'name']
}],
attributes: ['id', 'product_name', 'product_location', 'state_id', 'city'],
required: false
}],
attributes: ['id', 'name', 'code']
})
Change
attributes: ['id', 'product_name', 'product_location', 'state_id', 'city'],
to
attributes: ['id', 'product_name', 'product_location', 'state_id', 'city','country_id'],
Add country id in your product model attributes
How can i select a name column from another table instead of id ,
example :
class Attendance(db.Model):
__tablename__ = 'zk_attendance'
id = db.Column(db.Integer,primary_key=True)
uid = db.Column(db.Integer,db.ForeignKey('zk_users.uid'))
date = db.Column(db.Date)
time = db.Column(db.Time)
device = db.Column(db.Integer,db.ForeignKey('devices.id'))
user = db.relationship('Users',backref=db.backref('user', lazy='dynamic'))
class Users(db.Model):
__tablename__ = 'zk_users'
uid = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String)
And if i want to select all attendance :
#app.route('/api/attendance/<string:date_from>/<string:date_to>',methods=['GET'])
def get_attend_date_date(date_from,date_to):
data = db.session.query(Attendance).filter(Attendance.date.between(date_from,date_to)).order_by(Attendance.date,Attendance.time)
attendance_schema = AttendanceSchema(many=True)
data = attendance_schema.dump(data).data
return jsonify({'attendance':data})
OUTPUT
{
"attendance": [
{
"Device": 4,
"date": "2016-01-18",
"id": 18805,
"time": "00:49:00",
"user": 30025
},
{
"Device": 4,
"date": "2016-01-18",
"id": 18902,
"time": "00:49:00",
"user": 30045
},
BUT
am getting the user uid , i want to return the user.name
Am using flask_marshmallow to serialize the data before send it as josn , to be able send the user name , i have to nest the name from the users schema as the following :
class UsersSchema(ma.Schema):
name = fields.String(dump_only=True)
class AttendanceSchema(ma.Schema):
date = fields.Date(dump_only=True)
user = fields.Nested(UsersSchema)