Django 1.5 AbstractBaseUser with char primary key not JSON serializable - django

I am having the following custom user model trying to use the Django 1.5 AbstractBaseUser:
class Merchant(AbstractBaseUser):
email = models.EmailField()
company_name = models.CharField(max_length=256)
website = models.URLField()
description = models.TextField(blank=True)
api_key = models.CharField(blank=True, max_length=256, primary_key=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email','website']
class Meta:
verbose_name = _('Merchant')
verbose_name_plural = _('Merchants')
def __unicode__(self):
return self.company_name
The model works perfectly and database is as expected, but the problem is when I try to dumpdata to create fixtures for my tests.
python manage.py dumpdata --natural --exclude=contenttypes --exclude=auth.permission --indent=4 > fixtures/initial_data.json
Then I get the error:
CommandError: Unable to serialize database: <Merchant: Test Shop> is not JSON serializable
Do you have ideas what could be the reason for this. Could it be the charfield primary key or something with the abstractbaseuser model?
Thanks

After some time spend I found the problem. Actually it was not in Merchant model but in Product that has foreign key to Merchant:
class Product(models.Model):
name = models.CharField(max_length=200)
#merchant = models.ForeignKey(Merchant, to_field='api_key')
merchant = models.ForeignKey(Merchant)
url = models.URLField(max_length = 2000)
description = models.TextField(blank=True)
client_product_id = models.CharField(max_length='100')
objects = ProductManager()
class Meta:
verbose_name = 'Product'
verbose_name_plural = 'Products'
unique_together = ('merchant', 'client_product_id',)
def __unicode__(self):
return self.name
def natural_key(self):
return (self.merchant, self.client_product_id)
the natural_key method in the model returned self.merchant instead of self.merchant_id so it was trying to serialize the whole merchant object to create a natural key. After switching the natural_key method to the following one the problem was fixed:
def natural_key(self):
return (self.merchant_id, self.client_product_id)

Related

Unable to load fixture for through model django

What I am trying to do?
I have created a fixture for a through model and now I want to load it in my database.
What is the problem?
While loading the fixture using Django loaddata command for through model I get this error:
django.core.serializers.base.DeserializationError: Problem installing fixture
'm.json': ['“Dave Johnson” value must be an integer.']:(room.membership:pk=None)
field_value was 'Dave Johnson'
models.py
class Person(models.Model):
name = models.CharField(max_length=100, unique=True)
def natural_key(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=100, unique=True)
members = models.ManyToManyField(Person, through='Membership')
def natural_key(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
joined_on = models.DateTimeField(auto_now_add=True)
objects = MembershipManager()
def natural_key(self):
return self.person.name, self.group.name
I am creating fixture like this:
python manage.py dumpdata room.Membership
--natural-foreign --natural-primary > m.json
which creates the following json:
[
{
"model": "room.membership",
"fields": {
"person": "Dave Johnson",
"group": "Django Learning",
"joined_on": "2020-12-03T13:14:28.572Z"
}
}
]
I have also added get_by_natural_key method in the manager for through model like this:
class MembershipManager(models.Manager):
def get_by_natural_key(self, person_name, group_name):
return self.get(person__name=person_name, group__name=group_name)
And loading the fixture
python manage.py loaddata m.json
Other models are working fine. I can load them without any issue it is only the through model which is not working.
You are creating a model, not a manager. You should also ensure that the combination of fields is unique:
class MembershipManager(models.Manager):
def get_by_natural_key(self, person_name, group_name):
return self.get(person__name=person_name, group__name=group_name)
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
joined_on = models.DateTimeField(auto_now_add=True)
objects = MembershipManager()
class Meta:
constraints = [
models.UniqueConstraint(
fields=['person', 'contact'], name='unique_person_group'
)
]
def natural_key(self):
return self.person.name, self.group.name
For the Person and Group models, you will need managers as well:
class NameManager(models.Manager):
def get_by_natural_key(self, name):
return self.get(name=name)
class Person(models.Model):
name = models.CharField(max_length=100, unique=True)
objects = NameManager()
def natural_key(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=100, unique=True)
members = models.ManyToManyField(Person, through='Membership')
objects = NameManager()
def natural_key(self):
return self.name
After drilling down more on django source code about loaddata command I found out that it uses django deserialization internally. So I thought to read about serialization and deserialization which has this section in the docs that talks about dependencies during serialization.
So based on that following changes fixed the issue:
Solution:
Update the through model natural_key method and add natural_key dependencies:
def natural_key(self):
return self.person.natural_key(), self.group.natural_key()
natural_key.dependencies = ['room.Person', 'room.Group']
I also updated the natural_key of both Person and Group models to return tuple rather than single element i.e return self.name, . Moreover added managers for both the models.

Django Rest Framework: field name 'likes' is not valid for model 'userPost' improperlyConfigured

In my Django Rest Framework api I am attempting to add a property to my model UserPosts that returns all likes for said post. Despite my best efforts I keep running into this error. Here is my post model below:
class UserPosts(models.Model):
userProfile = models.ForeignKey(UserProfile, related_name="posts", on_delete=models.CASCADE)
image = models.ImageField()
caption = models.CharField(max_length=240)
#property
def get_likes(self):
from liked.models import Like
return Like(post=self)
and here is my like model:
class Like(models.Model):
user = models.OneToOneField(UserProfile, on_delete=models.CASCADE,)
post = models.ForeignKey(UserPosts, on_delete=models.CASCADE)
liked_at = models.DateTimeField()
and lastly the post serializer:
class postSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.UserPosts
fields = ('userProfile', 'image', 'caption', 'likes')
Thanks.
You have at least three ways. First as #WillemVanOnsem said, by the many_to_one change the likes to like_set
class postSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.UserPosts
fields = ('userProfile', 'image', 'caption', 'like_set')
# ^^^^^
Second rename your model's property to likes and fix the queryset inside it
class UserPosts(models.Model):
userProfile = models.ForeignKey(UserProfile, related_name="posts", on_delete=models.CASCADE)
image = models.ImageField()
caption = models.CharField(max_length=240)
#property
def likes(self):
# ^^^^^
from liked.models import Like
return Like.objects.filter(post=self).values() or []
# solution you try ^^^^^^^
return self.like_set.values() or []
# more django way
And Third, the most simple and elegant way for me, is to remove your get_likes and add the related_name to the post ForeignKey:
class Like(models.Model):
user = models.OneToOneField(UserProfile, on_delete=models.CASCADE,)
post = models.ForeignKey(UserPosts, related_name='likes', on_delete=models.CASCADE)
# ^^^^^^^^^^^
liked_at = models.DateTimeField()

Django REST framework, dealing with related fields on creating records

Preliminary note: this is a rather newbie question, though I have not found a sufficient answer on StackOverflow; many similar questions, but not this one. So I am asking a new question.
The problem: I'm having difficulty creating records where one field is a foreign key to an existing record, and I do not know what I'm doing wrong in my code.
In my app there are two models in question, a one-to-many relationship between Company and BalanceSheet:
models:
class Company(models.Model):
cik = models.IntegerField(default=0, unique=True)
symbol = models.CharField(max_length=4, unique=True)
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.symbol
class BalanceSheet(models.Model):
company = models.ForeignKey(Company,
null=True,
on_delete=models.CASCADE,
related_name='balance_sheets',)
date = models.DateField()
profit = models.BigIntegerField()
loss = models.BigIntegerField()
class Meta:
unique_together = (('company', 'date'),)
def __str__(self):
return '%s - %s' % (self.company, self.date)
serializers:
class BalanceSheetSerializer(serializers.ModelSerializer):
company = serializers.StringRelatedField()
class Meta:
model = BalanceSheet
fields = ('company','date','profit','loss')
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('cik', 'symbol', 'name')
Views:
class BalanceSheetCreate(generics.CreateAPIView):
model = BalanceSheet
queryset = BalanceSheet.objects.all()
serializer_class = BalanceSheetSerializer
urls include:
url(r'^(?P<symbol>[A-Z]{1,4})/create-balance-sheet/$', views.BalanceSheetCreate.as_view(),
name='create_balance_sheet'),
To this point, I have zero problem reading data. However, when trying to create records, I get errors I don't understand:
curl http://localhost:8000/financials/AAPL/create-balance-sheet/ -X POST -d "company=AAPL&date=1968-04-17&profit=1&loss=1"
IntegrityError at /financials/AAPL/create-balance-sheet/
null value in column "company_id" violates not-null constraint
Dropping the company data from that curl command results in the same error.
How do I get around this error? I thought I was telling the api what company I'm interested in, both explicitly in the url and in the post data.
Using python3.6, django 1.11, and djangorestframework 3.7.7
You get the IntegrityError because your code will try to create a new BalanceSheet without a company. That's because StringRelatedField is read-only (see docs) and therefore it's not parsed when BalanceSheetSerializer is used in write mode.
SlugRelatedField is what you need here:
class BalanceSheetSerializer(serializers.ModelSerializer):
company = serializers.SlugRelatedField(slug_field='symbol')
class Meta:
model = BalanceSheet
fields = ('company','date','profit','loss')
To answer my own question, here's what I wound up with. Thanks again go to dukebody.
models:
class Company(models.Model):
cik = models.IntegerField(default=0)
symbol = models.CharField(max_length=4)
name = models.CharField(max_length=255)
def __str__(self):
return self.symbol
class BalanceSheet(models.Model):
company = models.ForeignKey(Company,
null=True,
on_delete=models.CASCADE,
related_name='balance_sheets',)
date = models.DateField()
profit = models.BigIntegerField()
loss = models.BigIntegerField()
class Meta:
unique_together = (('company', 'date'),)
def __str__(self):
return '%s - %s' % (self.company, self.date)
serializers:
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('cik', 'symbol', 'name')
class BalanceSheetSerializer(serializers.ModelSerializer):
company = CompanySerializer(many=False)
class Meta:
model = BalanceSheet
fields = ('company', 'date', 'profit', 'loss')
def create(self, validated_data):
company_data = validated_data['company']
company, created = Company.objects.get_or_create(**company_data)
validated_data['company'] = company
sheet = BalanceSheet.objects.create(**validated_data)
return sheet
I also had to include the full company data within my curl statement as a nested dict.

Django admin Trouble saving new records with M2M fields - instance needs to have a primary key value before a many-to-many relationship can be used

Problem Statement:
I am using Django admin to manage many tables, some of which have many-to-many relationships. I am unable to save new records in tables (models) that have manytomany fields defined. I am able to render the add form just fine. The problem is only upon trying to save the record. I do not have the same problem when updating an existing record.
Using the models below, I receive the following error: 'Bout' instance needs to have a primary key value before a many-to-many relationship can be used.
The Bout model has a many-to-many relationship with the Equipment model. The BoutEquipment model is the intermediate model.
I've researched this problem high and low on StackOverflow and via Google, but am thus far unable to find a solution.
Disclosure: I'm new to Django and new-ish to Python. I'm hopeful that there's a relatively simple solution to this problem.
Thanks in advance.
models.py
class Bout(models.Model):
boutid = models.AutoField(db_column=u'BoutID', primary_key=True)
sessionid = models.ForeignKey(Session, db_column=u'SessionID', verbose_name=u'Session')
activitytypeid = models.ForeignKey(Activitytype, db_column=u'ActivityTypeID', verbose_name=u'Activity Type')
locationid = models.ForeignKey(Location, db_column=u'LocationID',verbose_name=u'Location')
equipment = models.ManyToManyField(Equipment, verbose_name=u'Related Equipment', related_name=u'Bout_Equipment', blank=True, null=True) #through = 'BoutEquipment'
intensitymetrics = models.ManyToManyField(Intensitymetric, verbose_name=u'Related Intensity Metrics', related_name=u'Bout_IntensityMetrics', blank=True, null=True) #through = 'BoutIntensitymetric'
def __unicode__(self):
return u'%s %s' % (self.sessionid, self.activitytypeid)
class Meta:
db_table = u'app_bout'
verbose_name = u'Bout'
verbose_name_plural = u'Bouts'
class Equipment(models.Model):
equipmentid = models.AutoField(db_column=u'EquipmentID', primary_key=True)
name = models.CharField("Name", max_length=100, db_column=u'Name')
equipmenttypeid = models.ForeignKey(Equipmenttype, db_column=u'EquipmentTypeID', verbose_name = u'Equipment Type')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_equipment'
verbose_name = u'Equipment'
verbose_name_plural = u'Equipment'
class BoutEquipment(models.Model):
id = models.AutoField(db_column=u'id', primary_key=True)
boutid = models.ForeignKey(Bout, db_column=u'Bout_ID')
equipmentid = models.ForeignKey(Equipment, db_column=u'Equipment_ID')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_bout_equipments'
admin.py
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentForm
inlines = [EquipmentShoeInline, EquipmentLogInline]
list_display = ('name','equipmenttypeid','manufacturer','retired','retiredby','retiredon','notes')
fields = (
'name',
('equipmenttypeid','manufacturer'),
('retired','retiredby','retiredon'),
'notes'
)
class BoutAdmin(admin.ModelAdmin):
form = BoutForm
filter_horizontal = ('equipment','intensitymetrics',)
list_display = ('sessionid','activitytypeid','locationid','sequence','activehand','baddata')
inlines = [BoutDeviceInline,]
fields = (
('sessionid','locationid','activitytypeid'),
'videofilelocation',
'sequence',
'activehand',
'notes',
'baddata',
('equipment','intensitymetrics')
)
A manytomany field in django is a join table between the two models you want to connect to each other.
This happens on SQL level, so both models have to exist in the database.
bout = Bout()
...
equipment = Equipment()
...
bout.equipment.add(equipment)
#fails because bout and equipment are not saved
bout.save()
bout.equipment.add(equipment)
#fails because equipment is not saved
equipment.save()
bout.equipment.add(equipment)
#yay :)

Django Rest Framework : Serializer doesn't find attributes

Here is my model:
class Address(models.Model):
"""
This is an Adress
"""
address_complete = models.CharField(max_length=100)
door_code = models.CharField(max_length=20, blank=True, null=True)
floor = models.IntegerField(blank=True, null=True)
infos = models.CharField(max_length=100, blank=True, null=True)
class Meta:
verbose_name_plural = "Addresses"
I created a serializer for this in serializer.py:
from rest_framework import serializers
from party_app.models import Address, UserProfile, Stuff, Event, Bringing, Quantity
class AddressSerializer(serializers.Serializer):
pk = serializers.Field()
address_complete = serializers.CharField(max_length=100)
door_code = serializers.CharField(max_length=20)
floor = serializers.IntegerField()
infos = serializers.CharField(max_length=100)
def restore_object(self, attrs, instance=None):
"""
Create or update a new UserProfile instance.
"""
if instance:
# Update existing instance
instance.address_complete = attrs.get('address_complete', instance.address_complete)
instance.door_code = attrs.get('door_code', instance.door_code)
instance.floor = attrs.get('floor', instance.floor)
instance.infos = attrs.get('infos', instance.infos)
return instance
# Create new instance
return Address(**attrs)
When I try to serialize an address using python manage?py shell, here is what I got:
>>> seria = AddressSerializer(Address)
>>> seria.data
AttributeError: type object 'Address' has no attribute 'address_complete'
Being new to DjangoRestFramework, I just don't know why I got this...
If you see something obvious, I would be glad to know it!!
Get rid of restore_object as you are using a Model it's not needed. Use the modelSerializer instead.
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = ('id', 'address_complete', 'door_code')