how to get mongoengine object id in flask program - python

i am using mongoengine to integrate with flask , i wanted to know how to get document object id every time i try i get File "/var/www/flask_projects/iot_mongo/app.py", line 244, in post return jsonify(user.id) AttributeError: 'BaseQuerySet' object has no attribute 'id'
class Test(Resource):
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('email',required=True, help='email')
args=parser.parse_args()
user=AdminUser.objects(email=args['email'])
return jsonify(user.id)
api.add_resource(Test,'/test')
if __name__ == '__main__':
app.run(debug=True)

I've been doing this. An example User model would be like~
class User(Document):
first_name = StringField(required=True, max_length=50)
last_name = StringField(required=True, max_length=50)
username = StringField(required=True)
password = StringField(required=True, min_length=6)
def to_json(self):
return {
"_id": str(self.pk),
"first_name": self.first_name,
"last_name": self.last_name,
"username": self.username,
"password": self.password
}
I convert the id to a string. I would then get a single object with~
user = User.objects.get(pk=user_id)
return user.to_json()
for a whole object, but if I just want the id I would do...
user.pk()
I created my own to_json method that converts the primary key to a string because otherwise it would return "id": ObjectID("SomeID") instead of neatly printing "id": "SomeID".
Hope this helps!
If you want to find someone by email I suggest~
User.objects.get(email=args['email'])

Check out the documentation, Document.objects is a QuerySet object.
You seem to be expecting that this part of your code
user=AdminUser.objects(email=args['email']) # user will be a QuerySet
will give you a single result, which is not the case, it will give you a QuerySet with zero or more results. It does not have an attribute id, this is why you get the error message you are seeing when you try to access this attribute here:
return jsonify(user.id) # QuerySet does not have the attribute id
You need to fetch the actual result(s) you want from it, assuming you are sure your query will return a single result, or do not care that there might be more than one result and just want the first one, you probably want something along these lines:
user=AdminUser.objects(email=args['email']).first() # extract first result
return jsonfiy(user)
Alernatively, returning all results would look like this:
users=AdminUser.objects(email=args['email']).all() # extract all results
return jsonfiy(users)

Related

Order object by field containing string value

I have a django model and a field representing an ip-address. I want order queryset of this model by ip-address value such "10.10.10.1"
I do Model.objects.order_by("ip_address"), but I get this
QuerySet["10.10.10.1", "10.10.10.11", "10.10.10.12", ...]
I want this QuerySet["10.10.10.1", "10.10.10.2", "10.10.10.3", ...]
I don't know how to do this.
Anyone have any ideas?
How I did it:
class IPAddressManager(models.Manager):
def get_queryset(self):
return super().get_queryset().extra(
select={'ordered_ip_addresses': "string_to_array(ip_address, '.')::int[]", },
)
def all_ordered_objects(self):
return self.order_by("ordered_ip_addresses")
class IPAddress(models.Model):
objects = IPAddressManager()
ip_address = models.CharField()
now when we will call IPAddress.objects.all_ordered_objects() we will have really ordered set

Flask / SqlAlchemy / Graphene - how to query an object from DB and change value?

I wish to open a GraphQL mutation endpoint where an id and string is sent in. This would then gather the item based on the ID and change a value in the object, and save the change in the item in the DB.
It would look something like this:
Query SomeMutation{
ExampleMutation(input: {id: 1, Status: "Something"}){
ExampleObject{
id
Status
}
}
}
I currently have the following setup:
(Schema.py)
class Mutation(graphene.ObjectType):
ExampleMutation = ExampleMutation.Field()
(Schema_ExampleObject.py)
class Captcha(SQLAlchemyObjectType):
class Meta:
model = ExampleObjectModel
interfaces = (relay.Node,)
(ExampleMutation.py)
class Attributes:
id = graphene.Int(description="Id")
status = graphene.String(description="Status")
class ExampleMutationInput(graphene.InputObjectType, Attributes):
pass
class ExampleSolution(graphene.Mutation):
ExampleObject= graphene.Field(lambda: ExampleObject, description="Example Object")
class Arguments:
input = ExampleMutationInput(required=True)
def mutate(self, info, input):
data = input
# **** Here I want to query an item from the DB based on the ID and change a value in it, then save it in the DB and return the new object to the GraphQL. ****
return ExampleMutation(exampleobject=exampleobject)
I looked up at solutions online and I saw library calls that would work in the following manner:
item = ExampleObject.query.filter(blablanla)
But the Object doesn't have such functions as "Query" so I'm left confused.
I have found the correct way of performing the operation:
query = ExameObject.get_query(info=info)
query = query.filter(ExampleObject.id == data.id)
example_object = query.first()

Make serializer for nested values in Django rest serializer

I am trying to integrate my django project with the api from mailchimp, to add users to a list I need to generate some json in the following format:
{
"email_address": "EMAIL",
"status": "subscribed",
"merge_fields": {
"FNAME": "FIRST_NAME",
"LNAME": "SURNAME"
},
Sadly I am having some struggles with the nested merge_field. I expected the following to work:
class MergeSerializer(serializers.Serializer):
FNAME = serializers.SerializerMethodField('get_first_name')
LNAME = serializers.SerializerMethodField('get_surname')
def get_first_name(self, obj):
return obj.first_name
def get_surname(self, obj):
return obj.surname
class CreateContactSerializer(serializers.Serializer):
email_address = serializers.EmailField()
status = serializers.SerializerMethodField('get_alternative_status')
merge_fields = MergeSerializer(read_only=True)
def get_alternative_status(self, obj):
return "subscribed"
This only generates some json with the email_address and the status, and completely ignores the merge_fields. After hours I have absolutely no clue what to try next. Does anybody know how to solve this problem?
Since I thought that the documentation for the marshmallow framework was a bit clearer I also tried it with their package, this however returend exactly the same result (so ignoring my merge_fields):
class MergeFieldsSchema(Schema):
FNAME = fields.String(attribute="first_name")
LNAME = fields.String(attribute="surname")
class CreateContactSerializer(Schema):
merge_fields = fields.Nested(MergeFieldsSchema)
email_address = fields.String()
status = fields.Constant("subscribed")
You don't say this, but I am assuming that surname and first_name are also part of the same object as email_address on your model, which is why the nested serializer does not work (as nested serializers are for foreign keys). If this is not the case, please add the model to the OP.
Because you just want to customize the output, you can use a Serializer Method on your main CreateContactSerializer:
class CreateContactSerializer(serializers.Serializer):
email_address = serializers.EmailField()
status = serializers.SerializerMethodField('get_alternative_status')
merge_fields = serializers.SerializerMethodField('get_merge_fields')
def get_alternative_status(self, obj):
return "subscribed"
def get_merge_fields(self, obj):
return {
"FNAME": obj.first_name,
"LNAME": obj.surname
}
If you want, you could even reuse the serializer that you already used and do
def get_merge_fields(self, obj):
serializer = MergeSerializer(obj)
return serializer.data;
Don't forget to add merge_fields to your fields

Foreign Key Resource from dynamic field

I've got an API endpoint called TrackMinResource, which returns the minimal data for a music track, including the track's main artist returned as an ArtistMinResource. Here are the definitions for both:
class TrackMinResource(ModelResource):
artist = fields.ForeignKey(ArtistMinResource, 'artist', full=True)
class Meta:
queryset = Track.objects.all()
resource_name = 'track-min'
fields = ['id', 'artist', 'track_name', 'label', 'release_year', 'release_name']
include_resource_uri = False
cache = SimpleCache(public=True)
def dehydrate(self, bundle):
bundle.data['full_artist_name'] = bundle.obj.full_artist_name()
if bundle.obj.image_url != settings.NO_TRACK_IMAGE:
bundle.data['image_url'] = bundle.obj.image_url
class ArtistMinResource(ModelResource):
class Meta:
queryset = Artist.objects.all()
resource_name = 'artist-min'
fields = ['id', 'artist_name']
cache = SimpleCache(public=True)
def get_resource_uri(self, bundle_or_obj):
return '/api/v1/artist/' + str(bundle_or_obj.obj.id) + '/'
The problem is, the artist field on Track (previously a ForeignKey) is now a model method called main_artist (I've changed the structure of the database somewhat, but I'd like the API to return the same data as it did before). Because of this, I get this error:
{"error": "The model '<Track: TrackName>' has an empty attribute 'artist' and doesn't allow a null value."}
If I take out full=True from the 'artist' field of TrackMinResource and add null=True instead, I get null values for the artist field in the returned data. If I then assign the artist in dehydrate like this:
bundle.data['artist'] = bundle.obj.main_artist()
...I just get the artist name in the returned JSON, rather than a dict representing an ArtistMinResource (along with the associated resource_uris, which I need).
Any idea how to get these ArtistMinResources into my TrackMinResource? I can access an ArtistMinResource that comes out fine using the URL endpoint and asking for it by ID. Is there a function for getting that result from within the dehydrate function for TrackMinResource?
You can use your ArtistMinResource in TrackMinResource's dehydrate like this (assuming that main_artist() returns the object that your ArtistMinResource represents):
artist_resource = ArtistMinResource()
artist_bundle = artist_resource.build_bundle(obj=bundle.obj.main_artist(), request=request)
artist_bundle = artist_resource.full_dehydrate(artist_bundle)
artist_json = artist_resource.serialize(request=request, data=artist_bundle, format='application/json')
artist_json should now contain your full artist representation. Also, I'm pretty sure you don't have to pass the format if you pass the request and it has a content-type header populated.

Populate a WTForms form object with a datetime.date

I'm cooking up a crud interface for an object representing a bill, as in the water bill, the electric bill, etc.
I'm using sqlalchemy to handle the data, wtforms to handle the forms, and flask to serve it.
Here's what my route looks like that serves the form for editing an existing bill:
#app.route('/edit_bill/<int:bill_id>', methods = ['GET'])
def edit_bill(bill_id):
s = Session()
bill = s.query(Bill).filter_by(id=bill_id).first()
form = BillForm(obj=Bill)
return render_template('edit_bill.html', form = form)
Using wtforms, I pass the bill object to the BillForm constructor, ensuring that the data representing the bill to be edited it populated to the form.
This is where it chokes. Here's the exception:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Bill.date_due has an attribute 'strftime'
Now, I've dipped into the python shell and queried up a bill to make sure that date_due has a datetime.date object on it, which is does. I use Jinja to build my front end, so I've looked into creating a template filter, but I don't know how that would work with wtforms, and it looks like sqlalchemy is the one choking anyway.
So what it do? I'm pretty confident I just need to figure out how to turn that datetime.date object into a string, but I'm not sure how to go about that.
Halp. Thanks!
Edit: Here's the BillForm class:
class BillForm(Form):
id = HiddenField()
name = TextField(u'Name:', [validators.required()])
pay_to = TextField(u'Pay To:',[validators.required()])
date_due = DateField(u'Date Due:',[validators.required()])
amount_due = IntegerField(u'Amount Due:', [validators.required()])
date_late = DateField(u'Late After:',[validators.required()])
amount_late = IntegerField(u'Late Amount:', [validators.required()])
date_termination = DateField(u'Termination Date:',[validators.required()])
And mapping class, too:
class Bill(Base):
__tablename__ = 'bills'
id = Column(Integer, primary_key=True)
name = Column(String)
pay_to = Column(String)
amount_due = Column(Integer)
date_due = Column(Date)
amount_late = Column(Integer)
date_late = Column(Date)
date_termination = Column(Date)
def __init__(self, name, pay_to, amount_due, date_due, amount_late, date_late, date_termination):
self.name = name
self.pay_to = pay_to
self.amount_due = amount_due
self.date_due = date_due
self.amount_late = amount_late
self.date_late = date_late
self.date_termination = date_termination
def __repr__(self):
return "<Bill ('%s', '%s', '%s', '%s')>" % (self.name, self.pay_to, self.amount_due, self.date_due)
Ah it took me some time to figure out where you went wrong, but think I found it out. Here's your code:
#app.route('/edit_bill/<int:bill_id>', methods = ['GET'])
def edit_bill(bill_id):
s = Session()
bill = s.query(Bill).filter_by(id=bill_id).first()
form = BillForm(obj=Bill)
return render_template('edit_bill.html', form = form)
Now, if pass a class as the obj kwarg in BillForm, the form gets populated with all kinds of strange objects. For example, if I replicate what you did and inspect form.date_due.data, it says it is an <sqlalchemy.orm.attributes.InstrumentedAttribute at 0x277b2d0> object. Like what is stated in the error message, this object does not have a strftime attribute.
So, your error is in line 5 of the code you presented. If you want to populate the form with the details of the bill object you retrieved in line 4, replace line 5 with form = BillForm(obj=bill). As you can see, the 'subtle' difference is the lowercase b in bill. I replicated your code and am convinced should fix the problem.
If you're interested, this is how I normally make edit views.
#app.route('/edit_bill/<int:bill_id>', methods = ['GET', 'POST'])
def edit_bill(bill_id):
s = Session()
bill = s.query(Bill).filter_by(id=bill_id).first()
form = BillForm(request.form, obj=bill)
if request.method == 'POST' and form.validate():
form.populate_obj(bill)
s.add(bill)
s.commit()
# Do some other stuff, for example set a flash()
return render_template('edit_bill.html', form = form)
I haven't used SQLAlchemy for a while so I might have made a couple of mistakes there. Hope this helps! If this answers your question 'accept' the answer.

Categories

Resources