0

I'm creating an API with flask_restful, and I want to search for example with two parameters that will be passed by GET, tag and author.

with the code below I can do this. However, it's necessary that I should pass the two parameters. I want whatever the parameter the user passed still search for it,

Exempli gratia: if I passed tag=tech the response should have all the news with tag tech and with all authors if also I passed the author it should consider tag as all -i think u got it-

class ArticleAPI(Resource):
    def get(self):
        tag=request.args.get('tag','')
        auth=request.args.get('author','')

        news = News.objects.filter(topic=tag,author=auth).to_json()
        return Response(news, mimetype="application/json", status=200)

i know i can do it long just like this, but it looks ugly :`(

    if tag is not None and auth is not None :
            news = News.objects.filter(topic=tag,author=auth).to_json()
        elif tag is not None :
            news = News.objects.filter(topic=tag).to_json()
        elif auth is not  None:
            news = News.objects.filter(author=auth).to_json()

I'm using Flask_mongoengine

from flask_mongoengine import MongoEngine

db = MongoEngine()

def initialize_db(app):
    db.init_app(app)

Simone
  • 813
  • 8
  • 21

1 Answers1

1

I think you are asking how to pass in keyword arguments into the .filter() method in a more concise way.

According to the mongoengine docs, .filter() is an alias for __call__(). It takes a Query object, or keyword arguments for the **query parameter. Your code is using the keywords style.

You could put the tag and auth variables into a dict, then unpack them using a double splat as keyword arguments.

Something like this:

fdict = dict()
if tag : fdict['tag'] = tag
if auth: fdict['auth'] = auth

news = News.objects.filter(**fdict).to_json()

Now you can add as many of these parameters as you want and it should be the same syntax.

You could just pass in all query params at once. This is the cleanest way I can think of:

news = News.objects.filter(**request.args).to_json()

That said, there is usually a security tradeoff for blindly taking user provided data and passing into a database. I don't know enough about how Mongo handles this to speak intelligently about what is best practice here. Plus, what you name on the UI side may not have the same name on the DB side.

This is also possible using a Query object, but I haven't done any MongoDB stuff in a long time and the syntax seems very specific, so I won't attempt here. :)

Jon
  • 1,820
  • 2
  • 19
  • 43
  • `filter` has a nice feature to select rows with __icontains like this `news = News.objects.filter(headline__icontains = headline).to_json()` is there any way to add this to dict or doing clean – Simone Mar 06 '20 at 17:05
  • 1
    yeah, just do `fdict['tag_icontains'] = tag` or whatever the field name is – Jon Mar 06 '20 at 17:07
  • the idea is i want to give the user 3 fields to search for an article input text, he can enter any key words, select option for tags, and select option for author. so any thing the user enter it should give him what he wants – Simone Mar 06 '20 at 17:07
  • If you are providing multiple possible keywords, you may want to look into the `Query` object syntax instead. You could do something like `Q(keyword='keyword1') | Q(keyword='keyword2')` to search for all possible variants. You could build this dynamically as well, but it will be more complicated. – Jon Mar 06 '20 at 17:22