ma.Nested probably does not work sqlalchemy and marshmallow - flask

I have one to many relationships between tables in a database and I want to get the data using an API.
I think that ma.Nested does not work because I don't get all the fields
can anyone help me?
I get only this :
[
{
"IsRef": false,
"commands": [
"299d7f0b-721c-484b-9448-072716a5fd70",
"382c5d9f-99a1-4aa2-ac30-96084e202fad",
"299d7f0b-721c-484b-9448-072716a5fd75",
"382c5d9f-99a1-4aa2-ac30-96084e202fak"
],
"filename": "uiyg",
"version": 8
}
]
this is the database model :
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(255), unique=True)
version = db.Column(db.Integer, unique=True)
IsRef = db.Column(db.Boolean)
commands = db.relationship('Command', backref='log', lazy='joined')
def __init__(self, filename, version, IsRef):
self.filename = filename
self.version = version
self.IsRef = IsRef
class Command(db.Model):
id_command = db.Column(db.String(255), primary_key=True, autoincrement=False)
name = db.Column(db.String(255))
log_id = db.Column(db.Integer, db.ForeignKey('file.id'))
status = db.Column(db.Boolean)
events = db.relationship('Event', backref='com', lazy='joined')
def __init__(self, id_command, name, log_id, status):
self.id_command = id_command
self.name = name
self.log = log_id
self.status = status
class Event(db.Model):
id_event = db.Column(db.Integer, primary_key=True)
event_name = db.Column(db.String(255))
seq_number = db.Column(db.Integer)
com_id = db.Column(db.Integer, db.ForeignKey('command.id_command'))
def __init__(self, event_name, seq_number, com_id):
self.event_name = event_name
self.seq_number = seq_number
self.com = com_id
this is the schema :
class EventSchema(ma.ModelSchema):
class Meta:
model = Event
fields = ('event_name', 'seq_number')
class CommandSchema(ma.ModelSchema):
class Meta:
model = Command
events = ma.Nested(EventSchema)
fields = ('id_command', 'name', 'status', 'events')
class LogSchema(ma.ModelSchema):
class Meta:
model = File
commands = ma.Nested(CommandSchema)
fields = ('filename', 'version', 'IsRef', 'commands')
Log_schema = LogSchema(many=True)
this is the API :
#app.route("/getall/<version>", methods=["GET"])
def get_all(version):
alle = File.query.filter_by(version=version)
return Log_schema.jsonify(alle)

this question is solved. I've changed 'lazy ='joined'' to 'lazy='dynamic'' and I've put ma.Nested out of the Meta class

Related

DRF model serializer foreign key return object, not ID

when serialising a foreign key object, I'll get the object ID instead of the object. Any advise on how to get the object excluding the PK is greatly appreciated.
Output:
"biosamples": [
{
"short_form": "BTO_0004725",
"label": "embryonic fibroblast",
"ontology": 1
}
],
Models:
class Biosample(models.Model):
biosample_id = models.AutoField(primary_key=True)
ontology = models.ForeignKey('Ontology', models.DO_NOTHING, related_name='biosample_ontologies')
short_form = models.CharField(max_length=255, unique=True )
label = models.CharField(max_length=255)
class Meta:
managed = True
db_table = 'biosample'
def __str__(self):
return self.label
class Ontology(models.Model):
ontology_id = models.AutoField(primary_key=True)
name = models.CharField(unique=True, max_length=255)
short_name = models.CharField(max_length=255)
url = models.CharField(max_length=255)
base_url = models.CharField(max_length=255)
rest_base_url = models.CharField(max_length=255)
prefix = models.CharField(max_length=255, unique=True)
class Meta:
managed = True
db_table = 'ontology'
def __str__(self):
return self.name
Serializers:
class OntologieSerializer(base.ObjectSerializer):
class Meta:
model = Ontology
fields = '__all__'
class BiosampleSerializer(base.ObjectSerializer):
class Meta:
model = Biosample
fields = '__all__'
ontology = OntologieSerializer(hidden=['ontology_id'])
ObjectSerializer (Data is read from a spreadsheet, strings need to be empty to load):
class ObjectSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
data = super().to_representation(instance)
# Need empty string for loading
return {key: ('' if data[key] is None else value) for key, value in data.items()}
Thanks!
Indentation error, ontologie ended upin class Meta
class BiosampleSerializer(base.ObjectSerializer):
class Meta:
model = Biosample
fields = '__all__'
#Culprit:
ontology = OntologieSerializer(hidden=['ontology_id'])

Flask sql alchemy get child object

When I declare a relationship with SQL-Alchemy, I don't found all the child's data when Marshmallow dumps the object. When Marshmallow deserialize the object, i would like to get my Member object to json not just the Ids.
My models :
class Company(db.Model):
__table_args__ = {"schema": "public"}
id_company = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
members = db.relationship('Member', back_populates="company", lazy='joined')
class Member(db.Model):
__table_args__ = {"schema": "public"}
id_member = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), nullable=False)
age = db.Column(db.Integer)
company_id = db.Column(db.Integer, db.ForeignKey('public.company.id_company'), nullable=False)
company = db.relationship("Company", back_populates="members",lazy='joined')
class MemberSchema(ma.ModelSchema):
class Meta:
model = Member
class CompanySchema(ma.ModelSchema):
class Meta:
model = Company
class CompanyIdInputSchema(Schema):
id_company = fields.Integer(required=True)
My route :
company_schema = CompanySchema()
pagination_schema = PaginationInputSchema()
company_id_schema = CompanyIdInputSchema()
class CompanyById(Resource):
#swag_from('swag_docs/swag_company_get.yml')
def get(self, id_company):
company_id_args = {"id_company": id_company}
errors = company_id_schema.validate(company_id_args)
if errors:
return {"error": str(errors)}, 500
else:
company = Company.query.filter_by(id_company=id_company)
company = companies_schema.dump(company)
if len(company)>=1:
return company, 201
else:
return 204
The result when a call the route is :
{
"members": [
1
],
"id_company": 1,
"name": "Total"
}
What i expect :
{
"members": [
{'id_member':1,'name':'Laurent','age':32,'company_id'=1}
],
"id_company": 1,
"name": "Total"
}
If you want the serializer to serialize nested field as a list of serialized objects instead of ids you need to add Nested field with many=True.
class CompanySchema(ma.ModelSchema):
members = ma.fields.Nested(MemberSchema, many=True)
class Meta:
model = Company

How to paginate a secondary table using SQLALCHEMY in Flask?

I am trying to paginate a secondary table for Client but it doesn't work, the results always more than the per_page parameter that by default shows only three items per page .
Here is my models.py:
subscribers = db.Table(
'clients_subscribed',
db.Column('client_id', db.Integer, db.ForeignKey('client.id', ondelete='CASCADE')),
db.Column('user_id', db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'))
)
class Client(db.Model, UserMixin):
id = db.Column(db.Integer(), primary_key=True)
public_id = db.Column(db.String(50), default=uuid.uuid4)
name = db.Column(db.String())
subscribed_users = db.relationship(
'User',
secondary=subscribers,
backref=db.backref('user', passive_deletes=True, lazy='dynamic')
)
Here is also my views.py :
from flask import current_app
current_app.config['SUBSCRIBERS_PER_PAGE'] = 3
#api_route.route('/client/subscribers/<string:category>/<string:client_id>', methods=['GET'])
#token_required
def subscribers(current_user, category, client_id):
if request.method == 'GET':
page = request.args.get('page', 1, type=int)
client = Client.query.filter_by(public_id=client_id).first()
if not client:
raise InvalidUsage(u'404, not found!', status_code=404)
pagination = # Paginate Client.subscribed_users
psubscribers = pagination.items
subscribers = {}
prev = None
if pagination.has_prev:
prev = url_for('api.subscribers', category=category, client_id=client_id, page=page-1, _external=True)
next = None
if pagination.has_next:
next = url_for('api.subscribers', category=category, client_id=client_id, page=page+1, _external=True)
for client in client.subscribed_users:
subscribers_case = {
'id': client.id,
'public_id': client.public_id,
'image': '/static/img/'+client.image if client.image else '/static/img/logo.png',
'name': client.name,
}
subscribers[client.public_id] = subscribers_case
return jsonify({
'subscribers' : subscribers,
'prev' : prev,
'next': next,
'count' : pagination.total
})
return jsonify({'error' : 'No data!'})
Also here is the class User in models:
class User(db.Model, UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer(), primary_key=True)
public_id = db.Column(db.String(50), default=uuid.uuid4)
name = db.Column(db.String())
family = db.Column(db.String())
bio = db.Column(db.String())
tele = db.Column(db.String(), unique=True)
password = db.Column(db.String())
clients = db.relationship('Client', backref='user', passive_deletes=True, lazy='dynamic')
news = db.relationship('News', backref='user', passive_deletes=True, lazy='dynamic')
notifications = db.relationship('Notification', backref='user', passive_deletes=True, lazy='dynamic')
image = db.Column(db.String(), nullable=True)
slug = db.Column(db.String())
roles = db.relationship(
'Role',
secondary=roles,
backref=db.backref('users', lazy='joined',
passive_deletes=True,
single_parent=True)
)
subscribed_clients = db.relationship(
'Client',
secondary=subscribers,
backref=db.backref('client', passive_deletes=True, lazy='joined')
)
Any suggestions guys how to make that work !!!
You need to use lazy=dynamic in the relationship in order to get a Query object back. Documentation here, and another StackOverflow question about this here.

How to serialize an object instance which gets the data from 2 separate models using Django Rest Framework?

Query :
I have a GET request which gives the data from the 2 models (single_tracklog_object in View). However, when I serialize this object instance using the serializers I am getting an empty data for devices as below.
{
"lat": "51.0038",
"lon": "8.0038",
"speed": 50,
"course": 5,
"device": {} # this needs to be having a value but is empty.
}
I am not understanding why its happening. Please guide me on this.
My Models :
class Tracklogs(models.Model):
tracklog_id = models.AutoField(primary_key=True)
device = models.ForeignKey(Tracking_devices, related_name='tracklogs')
lat = models.CharField(max_length=10, null=False)
lon = models.CharField(max_length=11, null=False)
.........
#timestamps
tracker_datetime = models.DateTimeField(blank=True, null=True)
class Meta:
get_latest_by = "tracker_datetime"
class Tracking_devices(models.Model):
device_id = models.AutoField(primary_key=True)
device_type = models.ForeignKey(Device_type)
user = models.ForeignKey(User, null=False)
name = models.CharField(max_length=100)
description = models.CharField(max_length=256, blank=True, null=True)
My View :
serializer_class = ShowLocationInfoSerializer
def get(self, request, *args, **kwargs):
# get the imei from the url
imei = self.kwargs['imei']
try:
single_tracklog_object = Tracklogs.objects.filter(device__imei = imei).values('lat', 'lon','speed','course','device','device__name').latest()
# Here its causing problem!!!
serializer = self.serializer_class(single_tracklog_object)
return Response(serializer.data)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
My Serializer:
class Tracking_DeviceSerializer(serializers.ModelSerializer):
name = serializers.CharField(read_only=True)
class Meta:
model = Tracking_devices
fields = ('name')
class ShowLocationInfoSerializer(serializers.ModelSerializer):
lat = serializers.CharField(read_only=True)
lon = serializers.CharField(read_only=True)
speed = serializers.IntegerField(read_only=True)
course = serializers.FloatField(read_only=True)
device = Tracking_DeviceSerializer()
class Meta:
model = Tracklogs
fields = ('lat', 'lon', 'tracker_datetime', 'speed', 'course', 'device' )
Thanks in advance.

DRF PUT request on unique model field

I have the following model:
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Hiren(models.Model):
movie = models.ForeignKey(Movie)
watched_full = models.BooleanField(default=True)
rating = models.IntegerField()
source = models.CharField(max_length=500, null=True)
watched_at = models.DateField()
quality_choice = (
..................
)
video_quality = models.CharField(max_length=3, choices=quality_choice)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
and serializer:
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
instance.movie.name = validated_data.get('movie', {}).get('name')
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.movie.save()
instance.save()
return instance
When I try to send a put request without changing name field from Movie model it throws an error
{
"movie": {
"name": [
"movie with this name already exists."
]
}
}
However, I can perfectly update any other field if I change the name field's value each time.
The problem is in Movie model defined by you.
When you set the name field of Movie model as unique = True,then any new entry with same movie name will always throw an error.
In your model,
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
If you want to add two entries with the same name,remove the line unique = True or make sure to save every entry with a different name.
Or,if you want to update the record/entry then you don't need to assign a value for name field,just remove that line from your code,alternatively check if the name of the movie is already same with an improvement in the code like this :
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
movie_name = validated_data.get('movie', {}).get('name')
if movie_name != instance.movie.name :
instance.movie.name = movie_name
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.save()
return instance
Hope this helps,Thanks.