Manipulate Request data of TastyPie and Django API - python

I'm writing an application with django-tastypie and following are my models.py and resource.py files.
Models.py:
import uuid
from django.db import models
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=50, null=False)
email = models.EmailField(max_length=254, null=False)
password = models.CharField(max_length=100, null=False)
role = models.CharField(max_length=16, default='basic', null=False)
def __unicode__(self):
return self.name, self.email
Resources.py:
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from api.models import User
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
authorization = Authorization()
excludes = ['password']
#allowed_methods = ['get']
Now the thing is that whenever I hit an API end point from postman, the user is created directly. Now what I don't understand is that whether the request data goes into resources and then into database or directly into the database? Actually, the thing is that I need to apply some changes to the data before it is stored in the database, like hashing the password and then storing the object in the database. I'm new to django, so how can I achieve that?
Like in Flask, we can do something like:
#user.route('/users', methods=['POST'])
def create_user(user_id):
data = request.get_json(force=True)
# do all the changes we want
user = User(data)
db.session.add(user)
db.session.commit()
Now if any request comes at '/users' endpoint, we can get it's data in the 'data' variable and then whatever changes we want before storing in the database. But how to do that in django with tastypie.
Any help would be appreciated

If you have to massage data before entering into database then Tastypie has the notion of hydrate and dehydrate methods.
Check that. Here is reference hydrate and dehydrate

In every web framework the data that sent with the request passed to the api endpoint through some mechanism and the same thing happens in Tastypie (you can read about it in Tastypie documentation under Flow Through The Request/Response Cycle).
If you want to change the data that you sending/receiving read about Hydrate/Dehydrate, in your case you want to use dehydrate on user password but I recommend you to save the effort and instead use custom user model by inheriting from AbstractUser, that way you can get hashed password by default when User object is saved to your DB.

Related

Django REST framework POST without insert

I'm building a Django REST API which has access to our existing database with existing users. The purpose of this API is allowing the upcoming mobile application to make requests.
I'm sending a post request to a view with custom authenticator to verify the sent account details.
My existing model:
class LogonAccount(models.Model):
id = models.BigIntegerField(primary_key=True)
email = models.TextField()
two_step_enabled = models.BooleanField()
password = models.TextField(blank=True, null=True)
username = models.TextField(unique=True, blank=True, null=True)
My View and Serializer
class LogonAccountViewSet(viewsets.ModelViewSet):
queryset = LogonAccount.objects.all().order_by('username')
serializer_class = LogonAccountSerializer
class LogonAccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = LogonAccount
fields = ('username', 'email')
Sending a post request to this endpoint with username and password in its body keeps returning me a bad request (400) with:
{
"username": [
"logon account with this username already exists."
],
"email": [
"This field is required."
]
}
Making the fields not required in the serializer just changes the error to database constraints (null value in column id)
class LogonAccountSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.CharField(required=False)
email = serializers.CharField(required=False)
class Meta:
model = LogonAccount
fields = ('username', 'email')
I'm not trying to insert data, just trying to validate it.
What am I doing wrong or how do I stop it from trying to insert data?
The error you are getting is not because the DRF is trying to insert data but because of the validations available in the serializer, you are using. You are getting two validation errors:
email is required. Since in your model, the email field is not allowing blank or null values, so in the serializer, this is treated as a required field. So you need to handle it. Either you send that in the post request or make that field non-mandatory.
username is violating a unique constraint. In your model, you have set the username field as unique. So when a serializer is generated this unique field validation is added to that field.
If you want to see the serializer generated for your model serializer and all the fields and validations for the serializer, you can use the following code:
ser = LogonAccountSerializer()
print(repr(ser))
After the comment from #spoontech. The DB operation is performed because you are using viewsets.ModelViewSet. If you just want to use the serializer for validating data, you can use it this way:
serializer = LogonAccountSerializer(request.data)
is_valid = serializer.is_valid()
if is_valid:
# do something using serializer.validated_data
else:
# access errors using serializer.errors()

How to Access Current User from Django to React?

I am developing a system where a user logs in and they can use the features of our service. We have used Django Authentication to make the User and Login backend.
This is the custom User model in my models.py -
class UserDef(AbstractUser):
def __str__(self):
return self.first_name + self.last_name
employeeId = models.IntegerField(unique=True)
email_address = models.EmailField()
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
USER_TYPES = [('1',"Admin"), ('2',"Director"), ('3',"Sr. Manager"), ('4',"Manager") , ('5',"Sr. Engineer"), ('6',"Assistant Regional Manager"), ('7',"Assistant to the Regional Manager")]
user_type = models.CharField(max_length=2048, choices=USER_TYPES)
created_at = models.DateTimeField(auto_now_add=True)
This in itself works really well and I was able to access all data as needed. I am writing my front-end in React though. Which means there's a certain disconnect between the templates html file and the actual React.js code. Which means I can't simply send the employeeId or some other attribute as context. Does anyone know how I can extract that User information from the Django DB (which we have connected to an RDS-MySQL server) and display it in my React app.
If the user is already logged in and a sessionID has been generated. You could just create an endpoint through where you can fetch the current user and then make a call to the endpoint in your React frontend.
#api_view(['GET'])
def current_user(request):
user = request.user
return Response({
'username' : user.username,
'firstname' : user.first_name,
'employeeID' : user.employeeId,
# and so on...
})
You could obviously, always use a serializer to ease the process.

Authentication and Authorization with Django GraphQL JWT and Graphene Relay

I would like to ask how I can do authentication and query tags limited to a specific user (owner) using Graphen Relay and Django JWT.
Here is my Model:
class Tag(models.Model):
"""Tag to be used for a objective"""
name = models.CharField(max_length=255)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='tag',
on_delete=models.CASCADE,
)
Here is my Schema code:
class TagNode(DjangoObjectType):
class Meta:
model = Tag
filter_fields = ['name']
interfaces = (relay.Node,)
Here is my Query:
class Query(graphene.ObjectType):
tag = relay.Node.Field(TagNode)
all_tags = DjangoFilterConnectionField(TagNode)
def resolve_all_tags(self, info):
# context will reference to the Django request
print(info.context.user)
if not info.context.user.is_authenticated:
return Tag.objects.none()
else:
return Tag.objects.filter(user=info.context.user)
Here is the document for Django JWT: https://github.com/flavors/django-graphql-jwt but I do not know how to get the user (already use info.context.user) but it does not work,
when I tried to add Authorization (JWT token) in the header or as an argument. It also does not work.
Basically you are doing everything correctly and your user should be fetched successfully.
I've just checked and it works in my case, I use JWT like a cookie but it doesn't matter.
Had you authorized your user when tried to get tags?
Because regarding JWT you need to authorize your user and receive the JWT token. Only after that the real user will be presented in the request when you call the tags query. Otherwise the user will be anonymous.

How to query whole dataset from PostgreSQL using Django models and managers & render frontend with Ajax

I would like to fetch data from a PostgreSQL using Django models/managers and use this data to render my frontend furthermore. I have the following plain simple example where I just get the defined privatekey value in return but not the other information from the table.
Estimated outcome:
I would like to get all elements of a single row as one object.
Models.py:
from django.db import models
import uuid
# Create your models here.
class AccountInformationManager(models.Manager):
pass
class AccountInformation(models.Model):
version = models.CharField(max_length=20, blank=False)
DID = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
accountNumber = models.IntegerField(blank=False)
broker = models.CharField(max_length=50, blank=False)
leverage = models.CharField(max_length=10, blank=False)
account_balance = models.FloatField(max_length=15, blank=False)
account_profit = models.FloatField(max_length=15, blank=False)
account_equity = models.FloatField(max_length=15, blank=False)
account_margin = models.FloatField(max_length=15, blank=False)
account_margin_free = models.FloatField(max_length=15, blank=False)
account_margin_level = models.FloatField(max_length=15, blank=False)
account_currency = models.CharField(max_length=20, blank=False)
objects = models.Manager()
class Meta:
db_table = 'AccountInfo'
Query.py:
from django.db import models
import sys
sys.path.insert(0, r"C:\Users\Jonas\Desktop\Dashex")
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'Dashex.settings'
import django
django.setup()
from Dashboard.models import AccountInformation
//// query data
account_information = AccountInformation.objects.all()
print(account_information)
Printed Output:
<QuerySet [<AccountInformation: AccountInformation object (30e61aec-0f6e-4fa0-8c1b-eb07f9347c1f)>, <AccountInformation: AccountInformation object (8b46c7c7-1bc8-4736-8dc5-7d5f012d594b)>]>
Process finished with exit code 0
Why it doesn't return the whole data from the table like broker, accountNumberetc.?
linked question:
Additionally, if I use AJAX + Javascript to render my frontend, would I then call the query.py script within my Ajax Url to fetch the data from the DB and render the frontend?
What you see when you print(obj) is a representation of the object. By default, Django represents a Model object by the Model class name (AccountInformation), the word object to indicate that it's an instance and the pk of the object (the UUID in your case).
You can change how an object is printed by overriding the __str__() method on your Model. In fact, you should always do that.
The QuerySet you get is actually an iterable so you can iterate over the items in the result and each item will be the full AccountInformation instance, e.g.
for item in account_information:
print(f"{item.DID}, version: {item.version}, number: {item.accountNumber} ...")
When you do an AJAX query, you should have a Django view to process the request and return a response to your client, as you would have for a normal browser page request. So no, your script is useful for testing purposes, but it should not be part of your app. Have you done the Django tutorial?
In result it's showing queryset object. You can iterate over the queryset to get each row.
for row in queryset:
print(row.broker, row.accountNumber)
For rendering to frontend, I will suggest to check the Django rest framework (DRF)
https://www.django-rest-framework.org/

ValueError: Lookup failed for model referenced by field

I have made Custom User model in my Django project. Here it is:
class CustomUser(User):
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.get_all_permissions()
And after it, I changed all Foreign Keys of user to new CustomUser model. It works OK. But I make one new migration and django cause error, when I want to migrate it:
ValueError: Lookup failed for model referenced by field blog.Comment.author: main.CustomUser
My blog.Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(CustomUser)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
What should I do?
Thanks!
Judging from the code you posted, you might be might be better served by extending the user model rather than replacing it. This pattern is usually called a profile model and works via a one-to-one relationship with User.
Profiles provides application specific fields and behaviors, while allowing User to go about it's usual business unchanged. It doesn't require you to muck around with rewriting auth or even necessarily change your foreign keys.
Here's an example of your code written as a profile:
class Profile(models.Model):
# Link to user :
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
def __str__(self):
return self.user.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.user.get_all_permissions()
Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
# How to access the profile:
def check_author(self):
self.author.profile.is_author()
You'll also want to add a signal to create a new profile when a user is registered:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
Django docs on extending users.
If a profile approach doesn't work for you, try inheriting from AbstractUser or AbstractBaseUser instead of User. The abstract models provide the same basic functionality as User and are the preferred technique for recent Django versions.
There are a handful of additional steps however, check out the docs on creating custom users for a run down.

Categories