I am new to python flask. I am creating an API for user registration where a user may have multiple languages. so I am referencing list of languages in my user object.
please get my code here..
class User(Document):
name = StringField(required=True)
email = EmailField(required=True, primary_key=True)
languages = ListField(ReferenceField(Language), required=True)
class Language(Document):
name = StringField(required=True, unique=True)
active = BooleanField(default=True)
#app.route("/register", methods=["POST"])
def register_user():
request_payload = request.json
user = User(
name = request_payload["name"],
email = request_payload["email"]
)
for lang in request_payload["languages"]:
user.languages.append(Language.objects(name=lang))
user.save()
**Post request Payload
======================**
{
"name":"xxxx",
"email":"xxx#gmail.com",
"languages":["English","Marathi"]
}
**Error:
========**
ValidationError (User:xxx#gmail.com) (A ReferenceField only accepts DBRef, LazyReference, ObjectId or documents: ['languages'])
Document object returns QuerySet ..it was causing that error. ..for loop changed and it worked
for lang in request_payload["languages"]:
obj = Languages.objects(name=lang)
# print("******" + str(obj.count()), file=sys.stdout)
if obj.count() == 0:
return error_return("Incorrect Language: "+lang+ " is not present in Languages collection", 400)
for l1 in obj:
user.languages.append(l1)
Related
I have the following database table:
import sqlalchemy as sql
class Users(db.Model):
__tablename__ = "users"
id = sql.Column(sql.Integer, primary_key=True)
username = sql.Column(sql.String,index=True, unique=True, nullable=False)
email = sql.Column(sql.String(120),index=True, unique=True, nullable=False)
last_seen = sql.Column(sql.DateTime, default=datetime.utcnow())
password = sql.Column(sql.String(128), nullable=False)
and I have the following schema:
class UserSchema(ma.SQLAlchemySchema):
class Meta:
model = Users
ordered = True
id = ma.auto_field(dump_only=True)
username = ma.auto_field(required=True,validate=validate.Length(min=3, max=64))
email = ma.auto_field(required=True, validate=[validate.Length(max=128),validate.Email()])
password = ma.auto_field(required=True, load_only=True,validate=validate.Length(min=5))
so I am trying to use pytest with flask to test if I can add a user and I generally get the error:
tests/test_users.py::test_create_user {"messages":{"json":{"email":["Missing data for required field."],"password_hash":["Missing data for required field."],"username":["Missing data for required field."]}}}
The function that I am using is:
def test_create_user():
username = "testing"
password = "12345678"
email = "testing#mail.io"
flask_app = create_app()
with flask_app.test_client() as test_client:
response = test_client.post("/api/users", data=json.dumps({
'username':username,
'password':password,
'email':email
}))
print(response.data.decode('utf-8'))
assert response.status == 201
assert b'id' in response.data
assert b'password' not in response.data
assert b'username' in response.data
I am doing this with postman and I am able to get the correct expected response. So How can I do this with pytest and flask?. I want to be get the expected responses with pytest.
Follow the instruction here.
In short:
Replace data= with json=
Don't create a json-string with json.dumps(), but pass in the dictionary.
The test client will create the json from it and pass it to your Flask code in the desired manner.
I want to list all roles a given user has.
I'm not looking for current_user nor has_role.
The idea is to make an 'edituser.html' where an admin can change/add/remove roles for a given user. For that use case I need to show what roles the user to be edited has.
I've read: Flask Security- check what Roles a User has but I don't understand how to use it in for example a route/view.
My models.py is like this.
class Role(db.Document, RoleMixin):
def __str__(self):
return self.name
name = db.StringField(max_length=80, unique=True)
description = db.StringField(max_length=255)
permissions = db.StringField(max_length=255)
class User(db.Document, UserMixin):
def __str__(self):
return self.username
username = db.StringField(max_length=255)
password = db.StringField(max_length=255)
active = db.BooleanField(default=True)
fs_uniquifier = db.StringField(max_length=64, unique=True)
confirmed_at = db.DateTimeField()
current_login_at = db.DateTimeField()
last_login_at = db.DateTimeField()
current_login_ip = db.StringField(max_length=255)
last_login_ip = db.StringField(max_length=255)
login_count = db.IntField(max_length=255)
roles = db.ListField(db.ReferenceField(Role), default=[])
user_datastore = MongoEngineUserDatastore(db, User, Role)
Here is something I do in my app:
#staticmethod
def get_users():
"""Return list of all users"""
attrs = (
"email",
"active",
"confirmed_at",
"create_datetime",
"update_datetime",
"last_login_at",
"current_login_at",
"current_login_ip",
)
query = current_app.sywuserdb.user_model.query
users = query.all()
# convert to a list of dict of interesting info
rv = []
for user in users:
# copy simple attributes
userdata = {}
for attr in attrs:
userdata[attr] = getattr(user, attr)
userdata["roles"] = [r.name for r in user.roles]
rv.append(userdata)
# users is a list of tuples - convert to list of dict
return {"status": 200, "msgs": [], "data": rv}
Change 'sywuserdb' to your data store ('user_datastore' in your question).
This is called as part of your API - I have an 'admin' blueprint that has the following endpoint defined:
#api.route("/users", methods=["GET"])
#auth_required("token", "session")
#roles_accepted("admin")
def get_users():
rv = get_users()
return flask.jsonify(rv), rv["status"]
Ignore the #staticmethod - that is since I have it as part of a UserFactory class since I have a bunch of admin methods and API to manage users.
I currently have a Django form that saves data from a questionnaire against a user, where a user is stored as a Foreign Key from the Person model. I can successfully find the person from the Person class using get_object_or_404(), but when I try to save(commit=True), the data is not being saved in the database. See below for my code:
# models.py
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=100)
email = models.EmailField(max_length=254, primary_key=True)
tel_number = models.CharField(max_length=13, blank=True)
referral_code = models.UUIDField()
class Meta:
verbose_name_plural = 'People'
def __str__(self):
return str(self.referral_code)
class Questionnaire(models.Model):
user = models.ForeignKey(Person, related_name='questionnaire_person', on_delete=models.CASCADE)
... and some questionnaire questions here (CharFields and TextFields) ...
# views.py
def index_questionnaire(request):
template = 'questionnaire.html'
# load blank instance of template
questionnaire = UserQuestionnaire()
context = {
"questionnaire": questionnaire
}
# if user has submitted something, check form is valid
if request.method == 'POST':
answers = UserQuestionnaire(data=request.POST)
if answers.is_valid():
# submission is genuine so save as new entry to database
# get user's unique referral ID from URL
user_referral_id = request.GET.get('user')
# check legit person
try:
answers.save(commit=False)
answers.person = get_object_or_404(Person, referral_code=user_referral_id)
print('user found: {}'.format(answers.person))
answers.save(commit=True)
print('Questionnaire saved')
except:
print("user not found")
return render(
request,
template,
context
)
#forms.py
class UserQuestionnaire(forms.ModelForm):
class Meta:
model = Questionnaire
fields = (
'answers_1',
'free_text_1',
'answers_2',
'answers_3',
'answers_4',
'answers_5',
'answers_6'
)
widgets = {
'answers_2' : forms.RadioSelect(),
'answers_3' : forms.RadioSelect(),
'answers_4' : forms.RadioSelect(),
'answers_5' : forms.RadioSelect(),
}
So at the moment I'm drawing the user parameter from the URL, which is uuid.uuid4(). The print statement in the "try: except" bit successfully prints out the user UUID as expected, yet when submitted it doesn't save correctly. For further info, I am using the MultiSelectField() for one of the questionnaire questions.
If anyone has any suggestions as to why this might be, that would be amazing!
That is because asnwers.save(commit=False) creates another new object.
Do something like
f = answer.save(commit=false)
f.person = get_object_or_404(Person, referral_code=user_referral_id)
f.save()
No need to do f.save(commit=True) since the True is default.
for more info check docs:
docs.djangoproject.com/en/3.1/topics/forms/modelforms/
Think that we have a big project with lots of apps which results in lots of queries and mutations. For such projects, how do you people handle the graphql code architecture. Let's take an example.
I have an app called accounts. It will have queries and mutation related to user and profile. The folder structure i am using is every app will have graphql folder which then will have schema.py and mutations.py. The code is arranged something like this for now
schema.py
class User(DjangoObjectType):
class Meta:
model = CustomUser
filter_fields = {
'email': ['exact', ],
}
exclude_fields = ('password', 'is_superuser', )
interfaces = (relay.Node, )
class ProfileNode(DjangoObjectType):
class Meta:
model = Profile
interfaces = (relay.Node, )
class UserQuery(object):
user = relay.Node.Field(User)
users = DjangoFilterConnectionField(User) # resolve_users is not needed now
class ProfileQuery(object):
profile = relay.Node.Field(ProfileNode)
profiles = DjangoFilterConnectionField(ProfileNode)
class UserProfile(ObjectType):
profile = Field(ProfileNode)
def resolve_profile(self, info, **kwargs):
if id is not None and info.context.user.is_authenticated:
profile = Profile.objects.get(user=info.context.user)
return profile
return None
class Viewer(ObjectType):
user = Field(User)
def resolve_user(self, info, **kwargs):
if info.context.user.is_authenticated:
return info.context.user
return None
mutations.py
class Register(graphene.Mutation):
"""
Mutation to register a user
"""
class Arguments:
first_name = graphene.String(required=True)
last_name = graphene.String(required=True)
email = graphene.String(required=True)
password = graphene.String(required=True)
password_repeat = graphene.String(required=True)
success = graphene.Boolean()
errors = graphene.List(graphene.String)
def mutate(self, info, first_name, last_name, email, password, password_repeat):
# console.log('info', info, first_name, last_name, email, password)
if password == password_repeat:
try:
user = CustomUser.objects.create(
first_name=first_name,
last_name=last_name,
email=email,
is_active=False
)
print ('user', user)
user.set_password(password)
user.save()
if djoser_settings.get('SEND_ACTIVATION_EMAIL'):
send_activation_email(user, info.context)
return Register(success=bool(user.id))
# TODO: specify exception
except Exception:
errors = ["email", "Email already registered."]
return Register(success=False, errors=errors)
errors = ["password", "Passwords don't match."]
return Register(success=False, errors=errors)
root schema
// just to show the number of mutations just for account apps.
from accounts.graphql.mutations import (
Activate,
DeleteAccount,
Login,
RefreshToken,
Register,
ResetPassword,
ResetPasswordConfirm,
)
from accounts.graphql.schema import Viewer, UserProfile
class Mutation(company_mutation.Mutation, graphene.ObjectType):
activate = Activate.Field()
debug = graphene.Field(DjangoDebug, name='__debug')
class Query(company_schema.Query, graphene.ObjectType):
viewer = graphene.Field(Viewer)
user_profile = graphene.Field(UserProfile)
debug = graphene.Field(DjangoDebug, name='__debug')
#staticmethod
def resolve_viewer(self, info, **kwargs):
if info.context.user.is_authenticated:
return info.context.user
return None
#staticmethod
def resolve_user_profile(self, info, **kwargs):
if info.context.user.is_authenticated and id:
return info.context.user
return None
schema = graphene.Schema(query=Query, mutation=Mutation)
yo can see the mutations just for accounts app. There are many mutations and there will be more when considering all the apps. How you people are dealing with such?
I think project organization based only on files like schema.py, queries.py, mutations.py is very bad for large project.
It's like organize your model parts, with files like models.py, fields.py, utils.py ...
Especially, a GraphQL ObjectType defined somewhere in your queries.py file can be returned or used as input by a mutation.
So I prefer a structure more based on objects and their logical relations
schema/
__init__.py : contains your root schema actual code
viewer.py : contains the ObjectType Viewer
user/
user.py : contains the ObjectType User and UserQuery
profile.py : contains the ObjectType Profil
account : contains the account management mutations
login.py : contains the login / logout mutations
...
Note : you can embed mutations the same way you did queries, to be able to get mutations queries like :
mutation {
account {
delete(id: 12) {
status
}
}
}
I am using MongoDB with Flask-MongoEngine as my ORM component to my web app.
I have structured the User document schema like so:
from ..core import db
class UserComics(db.EmbeddedDocument):
favorites = db.SortedListField(db.StringField(), default=None)
class UserSettings(db.EmbeddedDocument):
display_favs = db.BooleanField(default=False)
default_cal = db.StringField(default=None)
show_publishers = db.ListField(db.StringField(), default=None)
class UserTokens(db.EmbeddedDocument):
refresh_token = db.StringField(default=None)
access_token = db.StringField(default=None)
expire_time = db.StringField(default=None)
class User(db.Document, UserMixin):
# Save User document to this collection
meta = {'collection': 'users_test'}
userid = db.StringField()
full_name = db.StringField()
first_name = db.StringField()
last_name = db.StringField()
gender = db.StringField()
birthday = db.StringField()
email = db.EmailField()
friends = db.ListField(db.StringField())
date_creation = db.DateTimeField()
last_login = db.DateTimeField()
favorites = db.EmbeddedDocumentField(UserComics)
settings = db.EmbeddedDocumentField(UserSettings)
tokens = db.EmbeddedDocumentField(UserTokens)
However, When creating a new user like this (I have left out lines...):
def create_new_user(resp):
newUser = User()
....
newUser.settings.default_cal = resp['calendar']
....
newUser.save()
return
I run into this error:
AttributeError: 'NoneType' object has no attribute 'default_cal'
It seems to me that I am not using MongoEngines Embedded documents correctly but I do not know where I am going wrong.
Any help would be greatly appreciated!
Well you just have to create an embedded document object of the particular class, and then use it with the main document class, like so:
new_user = User()
user_settings = UserSettings()
user_settings.default_cal = resp['calendar']
new_user.settings = user_settings
# more stuff
new_user.save()
Note: Creating a new object only for the main document, does not automatically create the corresponding embedded document object(s), but while reading data ofcourse the case is different.
Edit:
As tbicr mentions below, we can also do this:
settings = db.EmbeddedDocumentField(UserSettings, default=UserSettings)
while declaring the field, that way we won't need to create the object as given in the first example.