Validate custom field with Flask-RESTPlus - flask

I'm trying to create a custom field for validating POSTed JSON in my API using Flask-RESTPlus 0.10.1
Below is the basic setup...
from flask_restplus import fields
import re
EMAIL_REGEX = re.compile(r'\S+#\S+\.\S+')
class Email(fields.String):
__schema_type__ = 'string'
__schema_format__ = 'email'
__schema_example__ = 'email#domain.com'
def validate(self, value):
if not value:
return False if self.required else True
if not EMAIL_REGEX.match(value):
return False
return True
I like the way the above documents in Swagger UI, but I can't seem to figure out how to actually use the validate method on it.
Here's how I'm using the custom field.
Json = api.model('Input JSON', {
'subscribers': fields.List(Email),
[...]
})
#api.expect(Json) // validate is globally set to true
def post(self):
pass
I've had luck using
'subscribers': fields.List(fields.String(pattern='\S+#\S+\.\S+')) instead, but this doesn't give me the control to customize the error message, where'd I'd like it to return that the field is not of the email type.
I've also gone on and added a custom validate_payload function (found within http://aviaryan.in/blog/gsoc/restplus-validation-custom-fields.html) that I call again within my POST method (instead of api.expect). This requires me to duplicate some core functionality and call it every time in addition to api.expect to output the proper Swagger documentation and a little bit of finesse to get it to work within nested fields.
It's my understanding that this should work out of box? Am I wrong? What am I missing here?

I appreciate this is a little old but just had the same issue.
It looks like the "validate" actually sat over a python jsonschema impl, if you're still interested in digging, it's available here
That aside - you can configure restplus API to use a better formatchecker as follows: (I also validate date-time and date)
format_checker = FormatChecker(formats=["date-time", "email", "date"])
api_v1 = Api(
app, version='1.4',
title='[Anon]',
description='[Anon] API for developers',
format_checker=format_checker
)

Related

Graphene-Django - how to pass an argument from Query to class DjangoObjectType

First of all, thanks ! it has been 1 year without asking question as I always found an answer. You're a tremendous help.
Today, I do have a question I cannot sort out myself.
Please, I hope you would be kind enough to help me on the matter.
Context: I work on a project with Django framework, and I have some dynamic pages made with react.js. The API I'm using in between is graphQL based. Apollo for the client, graphene-django for the back end.
I want to do a dynamic pages made from a GraphQL query having a set (a declared field in the class DjangoObjectType made from a Django query), and I want to be able to filter dynamically the parent with a argument A, and the set with argument B. My problem is how to find a way to pass the argument B to the set to filter it.
The graphQL I would achieved based on graphQL documentation
query DistributionHisto
(
$id:ID,
$limit:Int
)
{
distributionHisto(id:$id)
{
id,
historical(limit:$limit)
{
id,
date,
histo
}
}
}
But I don't understand how to pass (limit:$limit) to my set in the back end.
Here my schema.py
import graphene
from graphene_django.types import DjangoObjectType
class DistributionType(DjangoObjectType):
class Meta:
model = DistributionTuple
historical = graphene.List(HistoricalTimeSeriesType)
def resolve_historical(self, info):
return HistoricalTimeSeries.objects.filter(
distribution_tuple_id=self.id
).order_by('date')[:2]
class Query(object):
distribution_histo = graphene.List(
graphene.NonNull(DistributionType),
id=graphene.ID(),
limit=graphene.Int()
)
def resolve_distribution_histo(
self, info, id=None, limit=None):
filter_q1 = {'id': id} if id else {}
return DistributionTuple.objects.filter(**filter_q1)
I have tried few things, but I didn't find a way to make it to work so far.
At the moment, as you see, the arg "limit" reaches a dead end in def resolve*, where ideally, it would be pass up to the class DistributionSetHistoType where it would replace the slice [:2] by [:limit] in resolve_distribution_slice_set()
I hope I have been clear, please let me know if it's not the case.
Thanks for your support.
This topic called pagination.
front end seletion
const { loading, error, data, fetchMore } = useQuery(GET_ITEMS, {
variables: {
offset: 0,
limit: 10
},
});
backend selction
the number 10 in .count(10) represent the first 10 elements in the array
DistributionTuple.objects.filter(**filter_q1).count(10)

Is there a way to add custom validations on user-input data while using Flask-appbuilder inbuilt forms?

I am building a web-app using flask appbuilde and stuck at the following issue for a week now.
On the documentation, the instructions seem pretty straight forward:
class MyView(ModelView):
datamodel = SQLAInterface(MyModel)
validators_columns = {
'my_field1':[EqualTo('my_field2', message=gettext('fields must match'))]
}
However, when I implement this exactly, on UI it says "invalid input" even when inputs are correct thus hindering form submission.
My Code(views.py):
class DelModelView(ModelView):
datamodel = SQLAInterface(Dell)
base_filters = [['cap_id', EqualTo, get_user]] #current user
list_columns = ["cap_id", "s_code", "s_name", "sos", "date_of_change"]
#base_order = ("cap_id", "asc")
validators_columns = {
'cap_id':[FilterEqualFunction(get_user, message=_('fields must match'))]
}
def get_user():
return g.user.username
I want to add a validation that checks if while adding new entry cap_id == username.
Am I missing any link here? I have tried multiple solutions but nothing seems to work.
Any help will be appreciated!
I managed to solve this by doing the following:
I removed cap_id from add_coloumns. I am pre-populating it by using the default value, which is current_user and now it works perfectly fine. Yay!!

Flask Swagger documentation query parameter GET required

I'm using Swagger documentation with my flask project to document the endpoints and parameters.
To define the query parameters for an endpoint I'm doing:
#api.doc(params={
'name_query_parameter': 'Description'})
I wanted to know if it's possible for that parameter to show in the docs as "required", like it does for when the parameter is part of the path (home/name_query_parameter/something/something).
Looking into the documentation I only found the following:
#api.expect()
#api.doc(body=the_defined_payload)
But this implies for the information to be on the body, I can't have that with a GET request. Plus, I want it as a query parameter, not as part of the payload.
Is this possible at all?
Thanks.
The final solution to this is as follows, thanks to Mikhail for commenting about the parser. I have to admit though, documentation is not the best for flask-restplus.
I used the params part to make sure the fields appear in the docs along with a description and the parser for custom validation and to make the field appear as required even though it is located in the URL as params.
parser = reqparse.RequestParser()
parser.add_argument('superimportant',
type=inputs.boolean, location='args', required=True)
parser.add_argument('something', type=custom_validation_parser, location='args')
class MySuperClassResource(Resource):
#api.doc(parser=categories_by_retailer_parser,
params={"superimportant": "Description of this important field",
"something": "bla bla"
})
def get(self, blable):
parser.parse_args()
pass
The custom_validation_parser is just a method that allows custom validation, like for empty values. The format of that method is as follows. (It must return the value you want to access, and if there's . problem, raise a ValueError).
def custom_validation_parser(value):
if not value:
raise ValueError("Must not be empty.")
return value

Create/Update Tag for Intercom.io User in Python

Unfortunately there's no way to create a user in Intercom.io with a tag, so I'm trying to write some code that will look for an existing tag in Intercom, and if it's there, add a user to that tag, and if it's not, create the tag and add the user to it. I've tried several different variations by looking at the docs for the python-intercom library, but there are conflicting methods (Intercom.update_tag vs. Tag.update), and nothing has worked yet.
Here's how users are created in Intercom (this works):
import time
from members.models import Member
from intercom import Intercom, Tag
Intercom.app_id = settings.INTERCOM_TEST_APP_ID
Intercom.api_key = settings.INTERCOM_TEST_API_KEY
member = Member.objects.get(email="exampleemail#example.com")
Intercom.create_user(
email=member.email,
user_id=member.email,
name="%s %s" % (member.first_name, member.last_name),
created_at=int(time.time()),
city_name=member.city,
last_seen_ip=member.last_ip,
)
Here's what I currently have to look for and create or update tags, which triggers no errors, but doesn't successfully tag the user:
tag = Intercom.get_tag(name=member.referral_code)
if tag['id'] != None:
Intercom.update_tag(member.referral_code, "tag", user_ids=[member.pk])
else:
Intercom.create_tag(tag, "tag", user_ids=[member.pk])
I've also tried variations of the following, but it gets the error "descriptor 'update' requires a 'dict' object but received a 'unicode':
if Tag.find_by_name(member.referral_code) != 0:
Tag.update(member.referral_code, "tag", user_ids=[member.pk])
else:
Tag.create(member.referral_code, "tag", user_ids=[member.pk])
What do I need to change to get tagging to work?
My name's Jeff, I'm one of the customer success engineers at Intercom. Unfortunately the intercom-python library is still using our deprecated V1 API which is likely causing some of the confusion here. Until that library updates to use our newer REST API I would suggest that you use the python requests library and call our API directly. I've got minimal python experience but something like this should get you started on the right track.
import requests
from requests.auth import HTTPBasicAuth
import json
tags_url = 'https://api.intercom.io/tags'
app_id = 'YOUR_APP_ID'
api_key = 'YOUR_API_KEY'
headers = {'content-type': 'application/json', 'Accept': 'application/json'}
tag_name = 'My sweet tag'
# Get tags to then loop through
list_tag_response_as_json = requests.get(tags_url, auth=(app_id, api_key), headers=headers).json()
tag_names = [tag['name'] for tag in list_tag_response_as_json['tags']]
if tag_name not in tag_names
# Create a tag
tag_response = requests.post(tags_url, auth=(app_id, api_key), headers=headers, data={'name': tag_name})
# Tag users
tag_and_users = {'name':tag_name, 'users': [{'email': 'abc#example.com'}, {'email': 'def#example.com'}]}
tagged_user_response = requests.post(tags_url, auth=(app_id, api_key), headers=headers, data=tag_and_users)
Also, feel free to give us a shout in Intercom if you're still having trouble and we can help you there.

Importing django model methods in json

I am trying to output a set of database records in JSON as follows:
def json_dbtable(request, p):
t = MyModel.objects.filter({some query})
s = serializers.get_serializer("json")()
re = s.serialize(t, ensure_ascii=False)
return HttpResponse(re, mimetype="application/json")
However, one of the fields i'm trying to return needs to change if it is null, and to remedy this the model has a definition that is used as a property .e.g:
name = property(_get_useful_name)
So, to get to the crux of the question. How can I include this "name" property in my json serialization as well as the raw field data?
The short answer is no, the long answer, is you could serialize your MyModel instance yourself:
simplejson.dumps([{'pk': m.pk, 'name': m.name} for m in MyModel.objects.filter(...)])
I have written a serialization framework for Python called any2any
which include (de)serializers for Django
and which allows you to do that easily.
It will be way cleaner than the DIY way.
Hope that helps !