Django Rest Framework: Serializing splits list of strings into list of characters - django-rest-framework

I am setting up an API with DRF, everything is going smoothly but I am having a bit of a problem when passing a field with a list of strings
Json object
{
"postID": 1,
"index": 0,
"text": "For years you had a President who apologized for America – now you have a President who is standing up for America, and standing up for PENNSYLVANIA. Tomorrow, you have the power, with your vote, to save AMERICA! GET OUT AND VOTE!! #MAGA ",
"date": "2020-11-02",
"likesCount": 145000,
"commentsCount": 4500,
"sharesCount": 3500,
"hashtags": [
"[",
"'",
"M",
"A",
"G",
"A",
"'",
" ",
"'"
"A",
"G",
"A",
"I",
"N",
"'",
"]"
]
}
Whereas hashtags field values should be: "['MAGA' , 'AGAIN']".
How could I override the serializer and prevent that strings are split into characters ?
models.py
from djongo import models
from django.contrib.postgres.fields import ArrayField
# Create your models here.
class Post(models.Model):
index = models.IntegerField()
postID = models.IntegerField(primary_key=True)
text = models.CharField(max_length=500)
date = models.DateField()
likesCount = models.IntegerField()
commentsCount = models.IntegerField()
sharesCount = models.IntegerField()
hashtags = ArrayField(models.CharField(max_length=20, blank=True), size=50)
tokens = ArrayField(models.CharField(max_length=20, blank=True), size=50)
tagged = ArrayField(models.CharField(max_length=20, blank=True), size=50)
keywords = ArrayField(models.CharField(max_length=20, blank=True), size=50)
entities = ArrayField(models.CharField(max_length=20, blank=True), size=50)
noun_phrases = ArrayField(models.CharField(max_length=20, blank=True), size=50)
noun_phrases_keywords = ArrayField(models.CharField(max_length=20, blank=True), size=50)
serializer.py
from rest_framework import serializers
from .models import Post, Comments
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
Serializer inspection
PostSerializer():
postID = IntegerField(label='PostID', max_value=2147483647, min_value=-2147483648, validators=[<UniqueValidator(queryset=Post.objects.all())>])
index = IntegerField(max_value=2147483647, min_value=-2147483648)
text = CharField(max_length=500)
date = DateField()
likesCount = IntegerField(label='LikesCount', max_value=2147483647, min_value=-2147483648)
commentsCount = IntegerField(label='CommentsCount', max_value=2147483647, min_value=-2147483648)
sharesCount = IntegerField(label='SharesCount', max_value=2147483647, min_value=-2147483648)
hashtags = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Hashtags', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
tokens = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Tokens', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
tagged = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Tagged', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
keywords = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Keywords', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
entities = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Entities', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
noun_phrases = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Noun phrases', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])
noun_phrases_keywords = ListField(allow_empty=False, child=CharField(allow_blank=True, label='Noun phrases keywords', max_length=20, required=False), validators=[<django.contrib.postgres.validators.ArrayMaxLengthValidator object>])

I got to a solution using DRF serialize ArrayField as string as a reference :).
serializer.py
class StringArrayField(ListField):
def to_representation(self, obj):
# convert list to string
return "".join([str(element) for element in obj])
def to_internal_value(self, data):
data = data.split(",") # convert string to list
return super().to_internal_value(self, data)

Related

How to deduct funds from users withdrawals in Django Rest Framework

I am building a logistics web application using DRF and i want to set users to withdrawal certain amount, let's say a users withdraw $200, i want the users to get $180 this means minus $20 from each withdrawals, but when i tried using the API, i am getting $220 instead of $180, which means my code is returning a plus sign for withdrawals instead of negative sign.
Below is the codes from my Models
class UserWallet(models.Model):
wallet_id = models.UUIDField(unique=True, default=uuid.uuid4)
user = models.OneToOneField("accounts.User", on_delete=models.CASCADE, related_name="wallet")
currency = models.CharField(max_length=10, default="NGN")
created_at = models.DateTimeField(default=timezone.now)
def get_balance(self):
query = (Q(status="success") | Q(status="processing")) & Q(wallet=self)
balance = WalletTransaction.objects.filter(
query).aggregate(Sum('amount'))
return balance
def to_dict(self):
balance = self.get_balance()["amount__sum"]
return {
"wallet_id": self.wallet_id,
"balance": f"{balance:.2f}" if balance else "0.00",
"currency": self.currency,
"currency_symbol": "₦"
}
def get_earnings(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="payment").aggregate(Sum('amount'))
return total
def get_withdrawals(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="withdrawal").aggregate(Sum('amount'))
return total
def get_transfers(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="transfer").aggregate(Sum('amount'))
return total
def get_deposits(self):
total = WalletTransaction.objects.filter(
wallet=self, status="success", transaction_type="deposit").aggregate(Sum('amount'))
return total
Codes from the Views
#api_view(["POST"])
#transaction.atomic
def initialize_transaction(request):
payload = request.data
user = request.user
wallet = UserWallet.objects.get(user=user)
if payload["transaction_type"] == "deposit":
ser = DepositSerializer(data=request.data)
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=payload["amount"],
description="WALLET TOP UP",
transaction_type="deposit"
)
return Response({
"status": True,
"data": {
"email": user.email,
"amount": payload["amount"],
"reference": transaction.reference
}
})
if payload['transaction_type'] == "transfer":
ser = TransferSerializer(data=request.data, context={"request": request})
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=ser.validated_data["amount"]*-1,
description=payload['description'],
transaction_type="transfer",
extras=payload
)
otp = OTP.generate_otp(wallet)
task.otp_mail(request.user.email, {"code": otp})
data = {
"transaction_id": transaction.transaction_id,
}
return ApiResponse(data=data, message="otp sent").response()
if payload['transaction_type'] == "withdrawal":
ser = WithdrawSerializer(data=request.data, context={"request": request})
if not ser.is_valid():
return ApiResponse(message=error_to_string(ser.errors), data=ser.errors, status_code=400).response()
payload['bank'] = ser.validated_data['bank'].json()
transaction = WalletTransaction.objects.create(
wallet=wallet,
amount=ser.validated_data["amount"]*-1,
description=payload['description'],
transaction_type="withdrawal",
extras=payload
)
otp = OTP.generate_otp(wallet)
task.otp_mail(request.user.email, {"code": otp})
data = {
"transaction_id": transaction.transaction_id,
}
return ApiResponse(data=data, message="otp sent").response()
return ApiResponse(status_code=400, message="Invalid TransactionType").response()

How to limit the choice related to FK in DRF backend/admin panel

class Quiz(models.Model):
name = models.CharField(max_length=200,default="",blank=False)
questions_count = models.IntegerField(default="")
description = models.TextField(max_length=10000,default="",blank=False)
created = models.DateTimeField(auto_now_add=True,null=True,blank=True)
slug = models.SlugField(max_length=200,unique=True,blank=False)
roll_out = models.BooleanField(default=False)
def __str__(self):
return self.name
class Question(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE,default="",related_name='quiz')
label = models.CharField(max_length=2000,default="")
order = models.IntegerField(default="")
def __str__(self):
return self.label
class Answer(models.Model):
question =models.ForeignKey(Question, on_delete=models.CASCADE,default="")
correct = models.BooleanField(default="")
text = models.CharField(max_length=1000,default="")
def __str__(self):
return self.text
class QuizTaker(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE,default="")
user = models.ForeignKey(User, on_delete=models.CASCADE,default="")
correct_answers = models.IntegerField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
score = models.FloatField()
def __str__(self):
return self.user.username
class Response(models.Model):
quiztaker = models.ForeignKey(QuizTaker, on_delete=models.CASCADE,default="")
question = models.ForeignKey(Question, on_delete=models.CASCADE,default="")
answer = models.ForeignKey(Answer,on_delete=models.CASCADE,null=True,)
def __str__(self):
return self.question.label
These are my models, I have been following a tutorial on yt by Jay Coding.
I want to limit the answer choice in response model related to question in admin panel/ model so that there is choice of answer related to particular question.

Sql result set transformer is returning array of strings

How to return a json object instead of a json string array using SqlResultSetMappings or ResultTransformer. Here is the output i got when using SqlResultSetMappings or ResultTransformer
[
"Mel Bale ",
"B.A.",
"Interior Designer",
"Daosa",
"n/a"
]
what i want is like this
{
"name":"Mel Bale ",
"qualification":"B.A.",
"Job":"Interior Designer",
"location":"Daosa",
"phone":"n/a"
}
You need to define #SqlResultSetMappings like this so that your data are mapped as JSON
#SqlResultSetMapping(
name = "findAllDataMapping",
classes = #ConstructorResult(
targetClass = Sample.class,
columns = {
#ColumnResult(name = "name"),
#ColumnResult(name = "qualification"),
#ColumnResult(name = "Job"),
#ColumnResult(name = "location"),
#ColumnResult(name = "phone")
}
)
)

django rest framework:In Serializer how to show the field properties also

I have model:
class Ingredient(models.Model):
KILOGRAM = 'kg'
LITER = 'ltr'
PIECES = 'pcs'
MUNITS_CHOICES = (
(KILOGRAM, 'Kilogram'),
(LITER, 'Liter'),
(PIECES, 'Pieces'),
)
name = models.CharField(max_length=200,unique=True,null=False)
slug = models.SlugField(unique=True)
munit = models.CharField(max_length=10,choices=MUNITS_CHOICES,default=KILOGRAM)
rate = models.DecimalField(max_digits=19, decimal_places=2,validators=[MinValueValidator(0)],default=0)
typeofingredient = models.ForeignKey(TypeOfIngredient, related_name='typeof_ingredient',null=True, blank=True,on_delete=models.PROTECT)
density_kg_per_lt = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (kg/lt)',null=True,blank=True,validators=[MinValueValidator(0)])
density_pcs_per_kg = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (pcs/kg)',null=True,blank=True,validators=[MinValueValidator(0)])
density_pcs_per_lt = models.DecimalField(max_digits=19, decimal_places=2,verbose_name='Density (pcs/lt)',null=True,blank=True,validators=[MinValueValidator(0)])
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
When i get the api i also want to get field types like char, decimal, datetime etc
Something like the below api result, is it possible. Because i am using reactJs as frontend, i have tell the input what kind of field it can accept and also helps in sorting by text or number
{
"id": {value: 1,type: number},
"name": {value: "adark",type: charfield},
"rate": {value: "12.00",type: decimal},
"updated": {value: "2017-07-14T10:51:47.847171Z",type: datetime},
.......so on
}
The Corresponding Serializer would be as follows:
class IngredientSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
rate = serializers.SerializerMethodField()
updated = serializers.SerializerMethodField()
class Meta:
model = Ingredient
fields = ('name', 'rate', 'updated')
def get_name(self, obj):
response = dict()
response['value'] = obj.name
response['type'] = obj.name.get_internal_type()
return Response(response)
def get_rate(self, obj):
response = dict()
response['value'] = obj.rate
response['type'] = obj.rate.get_internal_type()
return Response(response)
def get_updated(self, obj):
response = dict()
response['value'] = obj.updated
response['type'] = obj.updated.get_internal_type()
return Response(response)

Getting unique objects using LINQ

I have a person class:
Public Class Person
{
public string Name {get; set;}
public string Id {get; set;}
}
Public EqualityOnPerson : IEqualityComparer<Person>
{
public bool Equals(PersonData x, PersonData y)
{
return x.Id == y.Id;
}
public int GetHashCode(Person obj)
{
return obj.Id.GetHashCode();
}
}
I have a list of person objects with duplicate Ids in it:
Person.Name = "ABC"
Person.Id = "123"
Person.Name = "CBA"
Person.Id = "123"
Person.Name = "DEF"
Person.Id = "456"
Person.Name = "GHI"
Person.Id = "789"
Person.Name = "JKL"
Person.Id = "789"
Person.Name = "MNO"
Person.Id = "789"
Person.Name = "PQR"
Person.Id = "101"
Person.Name = "STU"
Person.Id = "102"
Using distinct will give
Person.Name = "CBA"
Person.Id = "123"
Person.Name = "DEF"
Person.Id = "456"
Person.Name = "GHI"
Person.Id = "789"
Person.Name = "PQR"
Person.Id = "101"
Person.Name = "STU"
Person.Id = "102"
How do I get this
Person.Name = "DEF"
Person.Id = "456"
Person.Name = "PQR"
Person.Id = "101"
Person.Name = "STU"
Person.Id = "102"
Only those person objects which are not duplicated.
Regards.
You need to group the people using your equality comparer, then select people from groups with only one person in:
var unduplicatedPersons = persons
.GroupBy(p => p, new EqualityOnPerson())
.Where(g => g.Count() == 1)
.Select(g => g.Key);
Here's a dedicated extension method which may have a slight performance edge if required (just for fun):
public IEnumerable<T> DistinctOnly<T>(this IEnumerable<T> source,
IEqualityComparer<T> comparer)
{
HashSet<T> all = new HashSet<T>(comparer);
HashSet<T> distinct = new HashSet<T>(comparer);
foreach (T t in source)
{
if (all.Add(t))
distinct.Add(t);
else
distinct.Remove(t);
}
return distinct;
}
var result = persons.Where(outer =>
persons.Count(inner => inner.Id == outer.Id ) < 2)
This way you won't need an equality comparer.

Resources