I have the following Model and Serializer:
Model
class Location(models.Model):
company = models.ForeignKey(Company)
title = models.CharField(max_length=100)
class Meta:
ordering = ['company']
unique_together = ['company', 'title']
Serializer
class LocationSerializer(serializers.ModelSerializer):
company = serializers.StringRelatedField()
class Meta:
model = Location
When I try to create a new Location using the Serializer:
lo = LocationSerializer(data={'title': 'test', 'company': 2})
I get back the following error:
{'company': ['This field is required.']}
What gives? The only thing I'd changed recently in either the Model or the Serializer was adding the unique_together constraint to the Model. Why is the Serializer now unable to recognize the company value?
It turns out the error message is a complete red herring. What's really happening is that there's an incompatibility between the unique_together constraint in the Model and read-only fields like StringRelatedField defined in the Serializer.
Removing unique_together from the Model restored the Serializer's functionality. Alternatively, you could remove the read-only field declaration from the Serializer. Neither really seem like an appropriate solution as they require sacrificing functionality in pretty major ways.
Right now there appears to be an issue open on DRF's GitHub related to this problem but it remains to be seen if this is Working As Intendedtm or is in fact a bug.
Related
I have a serializer on my UserProfile model. It includes a SerializerMethodField "userGlobalPoints" :
class UserProfileSerializer(serializers.ModelSerializer):
userGlobalPoints = serializers.SerializerMethodField()
def get_userGlobalPoints(self, obj):
(*** calculation ***)
return userGlobalPoints
class Meta:
model = UserProfile
fields = '__all__'
In my Post serializer, I want to nest, for each post, information about the post's author. So I include fields from the UserProfile model (there is a ForeignKey relation in the Post model).
I would like to add the userGlobalPoints field. But I can't figure out how I can nest this field in the serializer. It looks like the usual syntax for nesting a serializer does not work on SerializerMethodField fields.
This is my PostSerializer:
class PostSerializer(serializers.ModelSerializer):
pk = serializers.ReadOnlyField()
authorNickname = serializers.CharField(source='postAuthor.userNickname', read_only=True)
authorProfileWithGlobalPoints = UserProfileSerializer(many=False, read_only=True)
class Meta:
model = Post
fields = ['pk','authorNickname','postText', 'postTimestamp', 'authorProfileWithGlobalPoints']
Any help on how to achieve this would be appreciated!
EDIT
Following a clever comment, the issue is not related to the SerializerMethodField. None of the fields of the nested serializer is serialized! I probably made a mistake in nesting the serializer in the reverse relation direction.
So the question does not need any more answers. Thank you all.
EDIT 2
The error was not caused by the MethodField. The issue is that I have nested serializers in the reverse direction of their relation. I tried to nest the author in the Post object, I should rather have nested the Post in the Author object. Since then I have changed my logic and it works.
Maybe an administrator could delete this topic, since it does not bring very much value to anybody.
What kind of error are you getting exactly?
Could it be that authorProfileWithGlobalPoints is not defined on the Post model? Maybe this solves it, by telling Django what to look for in the model:
class PostSerializer(serializers.ModelSerializer):
pk = serializers.ReadOnlyField()
authorProfileWithGlobalPoints = UserProfileSerializer(source='postAuthor', many=False, read_only=True)
class Meta:
model = Post
fields = ['pk','postText', 'postTimestamp', 'authorProfileWithGlobalPoints']
I have the following model
class Funcion(models.Model):
idPelicula = models.ForeignKey(Pelicula, on_delete=models.PROTECT)
idSala = models.ForeignKey(Sala, on_delete=models.PROTECT)
horaEntrada = models.TimeField(auto_now=False, auto_now_add=False)
horaSalida = models.TimeField(auto_now=False, auto_now_add=False)
fecha = models.DateField(auto_now=False)
And the next serializer
class FuncionSerializer(serializers.ModelSerializer):
pelicula = PeliculaSerializer(read_only=True)
idPelicula = serializers.PrimaryKeyRelatedField(write_only=True, queryset=Pelicula.objects.all(), source='pelicula')
sala = SalaSerializer(read_only=True)
idSala = serializers.PrimaryKeyRelatedField(write_only=True, queryset=Sala.objects.all(), source='sala')
class Meta:
model = Funcion
fields = '__all__'
When I try to post a new Funcion with the Django api root I get the following error:
TypeError at /funcion/
Got a `TypeError` when calling `Funcion.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `Funcion.objects.create()`. You may need to make the field read-only, or override the FuncionSerializer.create() method to handle this correctly.
I have used this method before for other project practices and it worked correctly.
If i delete the source='pelicula' argument for the PrimaryKeyRelatedField() it post the new funcion but when I do a get to bring al the data it doesn't show the Pelicula or Sala field just the rest
I tried deleting those arguments because the error at the end shows this message TypeError: Funcion() got unexpected keyword arguments: 'pelicula', 'sala'
The create method of the model manager does not allow "unexpected" fields. Those fields are not properties or fields of that model.
Unfortunately as far as I understand the code, the field slips through the field building functions and other into the validated_data dictionary. This is probably because every read_only field is allowed as it would be filtered out anyway before reaching the validated_data, but sourceing a read_only field goes through. I do not believe this is expected, but still causes no harm since the model managers' create method raises a TypeError.
To circumvent that issue you have two main options:
The source argument of your field may only reference fields or properties that exist on your model.
Pop any fields that are not part of the model out before being passed to the create method of the model.
The last option would look something like this:
class CoolSerializer(serializers.ModelSerializer):
not_cool = serializers.CharField(max_length=42)
def create(self, validated_data):
cool_again = validated_data.pop('not_cool', None)
# Do something with cool_again that you originally intended to do.
return super().create(validated_data)
While Deserialization of the nullable foreign key field. Getting the error :
'NoneType' object has no attribute 'foreign_field'
This issue was not coming till Django-Rest-Framework Version: 3.6.4
Found the cause of the issue(Removed None check and ObjectDoesNotExist check isn't handling None instance) : Link
Why this check is removed?
Is there any workaround that can handle the Nullable foreign key field?
Tried Following things :
Tried setting the default value: Didn't Work
Writing the Serializer Method isn't feasible(Lots of changes in existing Code).
Edit:
Model Structure:
class Demo(models.Model):
name = models.CharField(max_length=100)
class Demo1(models.Model):
demo = models.ForeignKey(Demo, default=None, blank=True, null=True, on_delete=models.CASCADE)
Serializer :
class Demo1(ModelSerializer):
demo = CharField(source='demo.name')
class Meta:
model=models.Demo1
fields = ('id', 'demo')
Found the solution :
class Demo1(ModelSerializer):
demo = CharField(source='demo.name', allow_null=True)
class Meta:
model=models.Demo1
fields = ('id', 'demo')
After putting allow_null in the serializer.it worked! but didn't understand, What is the point? if we have already put null=True in the model.
I'm getting a "DoesNotExist" error with the following set up - I've been trying to debug for a while and just can't figure it out.
class Video(models.Model):
name = models.CharField(max_length=100)
type = models.CharField(max_length=100)
owner = models.ForeignKey(User, related_name='videos')
...
#Related m2m fields
....
class VideoForm(modelForm):
class Meta:
model = Video
fields = ('name', 'type')
class VideoCreate(CreateView):
template_name = 'video_form.html'
form_class = VideoForm
model = Video
When I do this and post data for 'name' and 'type' - I get a "DoesNotExist" error. It seems to work fine with an UpdateView - or when an 'instance' is passed to init the form.
This is the exact location where the error is raised:
/usr/lib/pymodules/python2.7/django/db/models/fields/related.py in get, line 301
Does anyone know what might be going on?
Thanks
Since you have not posted your full traceback, my guess is that your owner FK is not optional, and you are not specifying one in your model form.
You need to post a full traceback.
I think it has to be class VideoForm(ModelForm) instead of VideoForm(modelForm).
If you aren't going to use the foreign key in the form use exclude = ('owner')
Using the following code:
class Organization(models.Model):
name = models.CharField(max_length="100",)
alias = models.SlugField()
...
class Division(Organization):
parent_org = models.ForeignKey(Organization)
class Meta:
unique_together=['parent_org', 'alias']
...
Trying to syncdb give me this error:
Error: One or more models did not validate:
organizations.division: "unique_together" refers to alias. This is not in the
same model as the unique_together statement.
Any help is appreciated,
Thanks,
Eric
This is by design. Reading the documentation for the unique_together option, it states that:
It's used in the Django admin and is enforced at the database level.
If you look at the table that a subclass creates, you'll see that it doesn't actually have the fields that its parent has. Instead, it gets a soft Foreign Key to the parent table with a field name called [field]_ptr_id, where [field] is the name of the table you're inheriting from excluding the app name. So your division table has a Primary Foreign Key called organization_ptr_id.
Now because unique_together is enforced at the database level using the UNIQUE constraint, there's no way that I know of for the database to actually apply that to a field not in the table.
Your best bet is probably through using Validators at your business-logic level, or re-thinking your database schema to support the constraint.
Edit: As Manoj pointed out, you could also try using Model Validators such as validate_unique.
[Model] Validators would work for you. Perhaps simplest, though, would be to use:
class BaseOrganization(models.Model):
name = models.CharField(max_length="100",)
alias = models.SlugField()
class Meta:
abstract = True
class Organization(BaseOrganization):
pass
class Division(BaseOrganization):
parent_org = models.ForeignKey(Organization)
class Meta:
unique_together=['parent_org', 'alias']
Note: as with your current code, you could not have subdivisions of divisions.
This is a solution I recently used in Django 1.6 (thanks to Manoj Govindan for the idea):
class Organization(models.Model):
name = models.CharField(max_length="100",)
alias = models.SlugField()
...
class Division(Organization):
parent_org = models.ForeignKey(Organization)
# override Model.validate_unique
def validate_unique(self, exclude=None):
# these next 5 lines are directly from the Model.validate_unique source code
unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
errors = self._perform_unique_checks(unique_checks)
date_errors = self._perform_date_checks(date_checks)
for k, v in date_errors.items():
errors.setdefault(k, []).extend(v)
# here I get a list of all pairs of parent_org, alias from the database (returned
# as a list of tuples) & check for a match, in which case you add a non-field
# error to the error list
pairs = Division.objects.exclude(pk=self.pk).values_list('parent_org', 'alias')
if (self.parent_org, self.alias) in pairs:
errors.setdefault(NON_FIELD_ERRORS, []).append('parent_org and alias must be unique')
# finally you raise the ValidationError that includes all validation errors,
# including your new unique constraint
if errors:
raise ValidationError(errors)
This does not strictly apply the question but is very closely related; unique_together will work if the base class is abstract. You can mark abstract model classes as such using:
class Meta():
abstract = True
This will prevent django from creating a table for the class, and its fields will be directly included in any subclasses. In this situation, unique_together is possible because all fields are in the same table.
https://docs.djangoproject.com/en/1.5/topics/db/models/#abstract-base-classes
I had similar challenge when trying to apply unique_together to a permission group created by a given client.
class ClientGroup(Group):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
is_active = models.BooleanField()
class Meta():
# looking at the db i found a field called group_ptr_id.
# adding group_ptr did solve the problem.
unique_together = ('group_ptr', 'client', 'is_active')