Not null constraint failed in create method of nested serializer - serialization

I'm trying to create an apiview to add data of a nested json in the database.
The models are:
class Catch(models.Model):
haul = models.ForeignKey('hauls.Haul')
weight = models.IntegerField()
category = models.ForeignKey('species.Category')
class Sex(models.Model):
catch = models.ForeignKey('catches.Catch', related_name='sexes')
sex = models.IntegerField()
class Length(models.Model):
sex = models.ForeignKey('samples.Sex', related_name='lengths')
length = models.IntegerField()
number_individuals = models.IntegerField()
The JSON I try to store in database is:
{
"catch_id":"6125",
"sex":"2",
"lengths": [
{
"catch_id":"6125",
"length": 24,
"number_individuals": 1
},
{
"catch_id":"6125",
"length": 25,
"number_individuals": 1
},
{
"catch_id":"6125",
"length": 26,
"number_individuals": 1
}
]
}
And the serializers involved are:
class LengthSerializer(serializers.ModelSerializer):
class Meta:
model= Length
fields = ['sex_id', 'length', 'number_individuals', ]
class SexCatchSerializer (serializers.ModelSerializer):
lengths = LengthSerializer(many=True)
class Meta:
model = Sex
fields = ['id', 'sex', 'catch_id', 'lengths', ]
# This is a nested serializer, so we have to overwrite the create function
def create(self, validated_data):
# Firstly, get the data from the nested parts
lengths_data = validated_data.pop('lengths')
# Secondly, save the Sex
sex = Sex.objects.create(**validated_data)
# Then, save the nested parts in its own models
Length.objects.create(sex=sex, **lengths_data)
# And finally, return the sex
return sex
But an error django.db.utils.IntegrityError: NOT NULL constraint failed: samples_sex.catch_id is returned in sex = Sex.objects.create(**validated_data) and I can't figure out why if the catch_id field is in the JSON and obviously is not null.

Related

How to save data using foreign key and retrieve full model in drf?

I am making follow-following logic in DRF Below are my codes.
Models.py
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
userId = models.UUIDField(primary_key = True,default = uuid.uuid4,editable = False,unique=True)
gender = models.CharField(max_length=1,default='M')
profilePic = models.URLField(max_length=200,default='https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png')
bio = models.TextField(null=True)
viewCount = models.IntegerField(default=0)
followers = models.IntegerField(default=0)
followings = models.IntegerField(default=0)
countryCode = models.CharField(max_length=255,default='+91')
country = models.CharField(max_length=255,default="India")
phoneNumber = models.CharField(max_length=10,default="0000000000")
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
class followAssociation(models.Model):
user = models.ForeignKey(CustomUser,related_name='user',on_delete=CASCADE)
follows = models.ForeignKey(CustomUser,related_name='follows',on_delete=CASCADE)
class Meta:
unique_together = ('user', 'follows')
Below are my serializers.
Serializers.py
from django.db.models import fields
from rest_framework import serializers
from users.models import CustomUser,followAssociation
class userSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('first_name','last_name','email','username','password','is_active','is_staff','is_superuser','bio','gender',
'viewCount','profilePic','userId','followers','followings','countryCode','country','phoneNumber')
read_only_fields = ['is_active', 'is_staff', 'is_superuser']
extra_kwargs = {'password': {'write_only': True, 'min_length': 4,'required': False},'username': {'required': False},'email': {'required': False}}
class followAssociationSerializers(serializers.ModelSerializer):
class Meta:
model = followAssociation
fields = ['user','follows']
Now my APIVIEW class
views.py
class followAssociationAPIView(APIView):
parser_classes = [JSONParser]
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request,format = None):
data = {"user":request.user.userId}
follows= get_object_or_404(CustomUser.objects.all(),userId = request.query_params["id"])
data["follows"] = follows.userId
followAssociation = followAssociationSerializers(data = data)
if followAssociation.is_valid(raise_exception=True):
followAssociation.save()
return Response(followAssociation.data,status=status.HTTP_202_ACCEPTED)
return Response(followAssociation.errors,status= status.HTTP_400_BAD_REQUEST)
def delete(self,request,format = None):
relation = get_object_or_404(followAssociation.objects.all(),follows = request.query_params["id"])
try:
data = followAssociationSerializers(relation)
relation.delete()
return Response(data.data,status=status.HTTP_200_OK)
except:
return Response(status=status.HTTP_302_FOUND)
In response, I get userId and FollowsId but I want the full user Model.
I tried depth = 1 and models.PrimaryRelatedFields() it works but only one time after I delete follow association object and next time I try to insert it says the username already exists. Please Help.
Try using Nested Serializers
You can create a new serializer class for the nested serializer and include only the fields you want. You can use the same serializer for both fields

Inner join using Django router

I have two models "Users" and "UserHasMachine" which are connected by primary key
I wanted to create queryset with fields from both of the tables filtered by a field "machine" field in "UserHasMachine" table
I tried to use select_releated but it seems to return just full "User" table without filtering by a field in "UserHasMachine"
models.py
class User(models.Model):
id = models.BigAutoField(primary_key=True)
email = models.CharField(unique=True, max_length=128)
name = models.CharField(max_length=256, blank=True, null=True)
pwd_hash = models.CharField(max_length=128, blank=True, null=True)
pwd_salt = models.CharField(max_length=256, blank=True, null=True)
is_builtin = models.IntegerField()
has_account = models.IntegerField()
is_verified = models.IntegerField()
is_enabled = models.IntegerField()
verifycode = models.CharField(max_length=128, blank=True, null=True)
user_creator = models.ForeignKey('self', models.DO_NOTHING)
is_deleted = models.IntegerField()
deleted_at = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
class Meta:
managed = False
db_table = 'user'
class UserHasMachine(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING, primary_key=True)
machine = models.ForeignKey(Machine, models.DO_NOTHING)
role = models.CharField(max_length=5)
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
class Meta:
managed = False
db_table = 'user_has_machine'
unique_together = (('user', 'machine'),)
views.py
class UserMachineViewSet(ListRetrieveUpdateModelViewSet):
queryset = UserHasMachine.objects.select_related('user')
serializer_class = UserMachineSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = ('machine_id', )
permission_classes = (partial(UserInAnyGroup, {
'update': ['admin'],
'partial_update': ['admin']
}),)
class UserViewSet(ListRetrieveUpdateModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = ('id', )
permission_classes = (partial(UserInAnyGroup, {
'update': ['admin'],
'partial_update': ['admin']
}),)
serializers.py
class UserMachineSerializer(serializers.ModelSerializer):
class Meta:
model = UserHasMachine
fields = '__all__'
read_only_fields = (
'user',
'machine',
'role',
'created_at',
'updated_at'
)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = (
'id',
'email',
'name',
'created_at',
'updated_at'
)
I expect to return a queryset containing name and the email from "User" table filtered by the machine field in the "UserHasMachines"
Here is the response I get then I call UserMachineViewSet:
...
{"JSON":[{"user":13,"role":"user","created_at":"2018-01-29T12:02:35","updated_at":"2018-01-29T12:02:35","machine":7},{"user":14,"role":"admin","created_at":"2018-01-29T12:02:35","updated_at":"2018-01-29T12:02:35","machine":7},{"user":39,"role":"user","created_at":"2018-10-02T13:39:12","updated_at":"2018-10-02T13:39:12","machine":7},
...
I think you have a misconception regarding select_related. All it does is to load user data from database when you are querying, so that you don't have to hit the database when accessing user information. For example:
queryset = UserHasMachine.objects.select_related('user')
for q in queryset:
q.user # will not hit database
queryset = UserHasMachine.objects.all()
for q in queryset:
q.user # will hit database
Now, if you want to show the user information in API, just add depth=1 in serializer meta class:
class UserMachineSerializer(serializers.ModelSerializer):
class Meta:
model = UserHasMachine
fields = '__all__'
read_only_fields = (
'user',
'machine',
'role',
'created_at',
'updated_at'
)
depth = 1
Then it will show nested fields inside user field.
BTW I am not clear about what you meant by:
I expect to return a queryset containing name and the email from "User" table filtered by the machine field in the "UserHasMachines"
But the User information is already there in the queryset, and you can access it via:
# method 1
queryset = UserHasMachine.objects.select_related('user')
for q in queryset:
print(q.user.id)
# method 2
queryset.values('user__id', 'user__email')
# method 3
# you can annotate any value of user with queryset like this:
from django.db.models import F
queryset = queryset.annotate(user_email=F('user__email'))

Operational Error 1054 Unknown Column

class ProjectTimeAllocation(models.Model):
id = models.IntegerField(primary_key=True)
project_id = models.ForeignKey(Projects, models.DO_NOTHING, related_name='pj_id', blank=True, null=True)
project_manager_id = models.ForeignKey(ProjectManagers, models.DO_NOTHING, blank=True, null=True)
hours = models.IntegerField(blank=True, null=True)
week = models.DateTimeField(blank=True, null=True)
date_created = models.DateTimeField(auto_now=True, null=True)
last_updated = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'project_time_allocation'
views.py
def timeallocation(request, project_manager_id):
#time_list = ProjectTimeAllocation.objects.order_by('-id')
if request.method == "POST":
form = ProjectTimeAllocationForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
#post.project_manager_id = project_manager_id
post.save()
messages.success(request, "Updated Time!")
return HttpResponseRedirect('/dashboard/')
else:
form = ProjectTimeAllocationForm()
return render(request, 'dashboard/timeallocation.html', {'form':form})
forms.py
class ProjectTimeAllocationForm(forms.ModelForm):
week = forms.DateField(widget=forms.SelectDateWidget())
class Meta:
model = ProjectTimeAllocation
fields = (
'hours',
'week',
'project_manager_id',
'project_id',
)
widgets = {
}
labels = {
}
I get an operational error (1054, "Unknown column 'project_id_id' in field list. I don't understand where the extra _id is coming from? I have tried clearing the migrations and making new migrations.
It appears that django appends _id on ForeignKeys and thus I had to remove _id from my column name in the model and in the form and then django appended it back on when it generated the sql insert statement and the problem is now resolved.

one2many contains one2many how to set the value

I want to directly add the following model in onchange value
The data is from other models
My model
Class A:
_name = 'budget.budget'
main_materials_list = fields.One2many(comodel_name='budget.main.material', inverse_name='budget_id', string=u'A')
#api.onchange('suite_series')
def onchange_suite_series(self):
self.main_materials_list = self._materialValue()
def _materialValue(self):
page = []
for item in self.suite_series.main_material_ids:
product_ids = []
data = {
'product_id':self.main_materials_list.product_ids
}
for val in item.product_id:
product_item = {
'product_id': val.product_id.id
}
product_ids.append((0, 0, product_item))
data.update({'product_ids': product_ids})
page.append((0, 0, data))
return page
Class B:
_name = 'budget.main.material'
product_ids = fields.One2many(comodel_name='budget.material', inverse_name='main_id', string=u'B')
Class C:
_name = 'budget.material'
product_id = fields.Many2one(comodel_name='product.product', string=u'C')
Using the above method, then the second one2many assignment is not successful!

Remove item of record without knowing all item

I have this simulation:
init : (Model, Cmd Msg)
init = ({ dog = List Dog }, Cmd.none)
type alias Dog =
{ name : String
, age : Int
, price : Float
, extra = List Extra
}
type alias Extra =
{ allergies : List String
, wishes : List String
}
[{ name = "Hot"
, age = 1
, price = 300.5
, extra = [{...}]
},
{ name = "Dog"
, age = 3
, price = 150.0
, extra = [{...}]
}]
And I want to remove only 'extras' of Dog, in determined part of the code:
[{ name = "Hot"
, age = 1
, price = 300.5
},
{ name = "Dog"
, age = 3
, price = 150.0
}]
I can do this by mapping the entire list and generating a new one by removing 'extra' occurrence:
removeExtraOfDogs dogList =
(dogList |> List.map (\dog ->
{ name = dog.name
, age = dog.age
, price = dog.price
}
))
but I want to make it dynamic to just pass the extra to remove, without having to know what variables there are in the type and recreate it
Elm used to have this feature but it was removed a while ago. But, based on your use case described in a comment, I don't think you need this feature. You can instead use extensible record feature of Elm to allow passing different records into a function as long as they contain a fixed set of fields.
For example, let's say you have two types with name and age fields and having an extra incompatible field:
type alias Foo = { name : String, age : Int, extra : List String }
type alias Bar = { name : String, age : Int, extra : Int }
You can define a function that takes a record with a name field of type String and age of type Int and any extra fields:
encode : { r | name : String, age : Int } -> String
encode record = record.name ++ "," ++ toString record.age
You'll can now pass both Foo and Bar to this function because they both satisfy the requirements of the type signature.