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')
Related
I am using Django Rest Framework to create some api's. I am using factory boy to create test instances. I have an Abstract model called base_model which is inherited by all other models of the project.
created_at = models.DateTimeField(editable=False)
updated_at = models.DateTimeField(editable=False)
class Meta:
abstract = True
ordering = ['id']
def save(self, *args, **kwargs):
if not self.created_at:
self.created_at = timezone.now()
self.updated_at = timezone.now()
super(BaseModel, self).save(*args, **kwargs)
My client Model
from django.db import models
from mtl_manager.api.base_model import BaseModel
from mtl_manager.projects.enums import ProjectStatus
class Client(BaseModel):
client_name = models.CharField(max_length=250, blank=False)
phone_number = models.CharField(max_length=250, blank=False)
email = models.EmailField(blank=False, unique=True, null=False)
addressLane1 = models.TextField()
This model worked. I was able to create retrieve and list client objects . Now I was about to unit test the routes and started with creating instance using Factory boy
class ClientFactory(DjangoModelFactory):
name = Faker("company")
gst = "323232";
phone_number = Faker("phone_number")
zipCode = "686542"
address_lane = Faker("street_address")
registration_number = "32313094839483"
state = "kerala"
country = Faker("country")
class Meta:
model = Client()
This raises error Attribute-error: 'Client' object has no attribute '_default_manager'.
But from my console I verified if client has default manager using
In [11]: Client.objects
Out[11]: <django.db.models.manager.Manager at 0x7fe4fc6d7bb0>
You need to pass a reference to the Client class, not construct a Client object, the parenthesis in model = Client() thus should be removed:
class ClientFactory(DjangoModelFactory):
# …
class Meta:
model = Client
I'm trying to send the following HTTP Post API request to create a new EventInterest object. How can I accomplish this in a smallest payload instead of sending the entire object? I'm attempting an extra layer of security-through-obfuscation and instead of using the default integer pk, how can I use uuid for Event and username for User? .... Or do the extra SQL lookups negate the benefits of simplifying the payload and I should just use pk?
models.py
class Event(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(max_length=500)
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, db_index=True, blank=True)
class EventInterest(models.Model):
event = models.ForeignKey(Event)
sender = models.ForeignKey(settings.AUTH_USER_MODEL) # from User
api.py
class EventInterestViewSet(mixins.CreateModelMixin, GenericViewSet):
queryset = models.EventInterest.objects.all()
serializer_class = serializers.EventInterestSerializer
lookup_field = 'uuid'
serializer.py
class EventInterestSerializer(serializers.ModelSerializer):
# event = serializers.SlugRelatedField(read_only=True, slug_field='uuid')
# recipient = serializers.SlugRelatedField(read_only=True, slug_field='username')
# sender = serializers.SlugRelatedField(read_only=True, slug_field='username')
class Meta:
model = models.EventInterest
fields = (
'event', #works with pk, want uuid
'sender', # works with pk, want username
)
HTTP Post:
{
"event": "da9290c6-f6f8-4d27-bfe0-d388ed911fe8",
"sender":"eX8gkxJNDREv" //this is the username field
}
You need to make your UUIDField as primary key. Just like this:
class Event(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
....
As you defined uuid in Event model, you have to define uuid in User model too. In order to do that, you have to extend the default user model. Then you have to override the create() method of EventInterestSerializer to do a lookup on respective UUID field instead of pk
models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, db_index=True)
class Event(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(max_length=500)
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, db_index=True)
class EventInterest(models.Model):
event = models.ForeignKey(Event)
sender = models.ForeignKey(User)
serializer.py
class EventInterestSerializer(serializers.ModelSerializer):
class Meta:
model = EventInterest
fields = ('event', 'sender',)
def create(self, validated_data):
try:
return EventInterest.objects.get(event__uuid=validated_data['event'],
sender__uuid=validated_data['sender'])
except EventInterest.DoesNotExist:
raise serializers.ValidationError("No matching data found")
I'm creating a Django (1.8) webapp that saves racing laptimes and scoreboards. The database is populated using an API built using Django Rest Framework. It's the first time I'm trying to build a proper api using rest framework.
A quick overview of the models:
Event, A racing event/weekend
Session, A single race/practice/quali - FK Event
Car, A car taking part in a session - FK Session
Lap, Laps for specific car - FK Car
The Event is created manually, but the rest is supposed to be "dynamic" (get or create)
Right now I'm trying to create a new car using my API, but I'm stuck. To get the cars event and session I'm trying to use the url;
/api/results/skrotbilsracet-29042016/r1/cars/
The idea is to post data to this url and "get or create" a new car object.
To get the correct session object for the new car session FK, I need to use a custom function that takes the kwargs and tries to find the session.
The more I read about how to solve this, the more confused I get.
Could someone push me in the right direction?
This is my latest attempt at solving this, which just gives me "{"session":["This field is required."]}"
models.py
class Session(models.Model):
session_types = (
('p', 'Practice'),
('q', 'Qualification'),
('r', 'Race')
)
event_id = models.ForeignKey(Event, related_name='sessions')
name = models.CharField(max_length=2, blank=True)
current_session = models.BooleanField(default=True)
session_type = models.CharField(max_length=2,
choices=session_types)
started = models.DateTimeField(auto_now_add=True)
ended = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['started']
def save(self):
if not self.name:
# Get number of sessions
session_count = Session.objects.filter(event_id=self.event_id)\
.filter(session_type=self.session_type)\
.count()
session_count += 1
self.name = self.session_type + str(session_count)
super(Session, self).save()
def __unicode__(self):
string = self.started.strftime("%d-%m-%Y %H:%M") + ' - '
string += self.name.upper()
return(string)
class Car(models.Model):
session = models.ForeignKey(Session, related_name='cars')
number = models.IntegerField()
full_name = models.CharField(max_length=256, blank=True)
short_name = models.CharField(max_length=256, blank=True)
race_class = models.CharField(max_length=50, blank=True)
best_lap = models.IntegerField(blank=True, null=True)
best_lap_time = models.CharField(max_length=20, blank=True)
best_sector1 = models.CharField(max_length=20, blank=True)
best_sector2 = models.CharField(max_length=20, blank=True)
best_sector3 = models.CharField(max_length=20, blank=True)
best_speed = models.IntegerField(blank=True, null=True)
pitstops = models.IntegerField(blank=True, null=True)
total_time = models.CharField(max_length=20, blank=True)
transponder = models.CharField(max_length=50, blank=True)
apiUrls.py
urlpatterns = [
url(r'^raceslug/$', raceSlugView.as_view(), name='race-slug'),
url(r'^events/$', eventsView.as_view(), name='event-list'),
url(r'^session/$', getSessionView.as_view(), name='session-pk'),
url(r'^(?P<event_id>[a-z0-9\-]+)/$', eventView.as_view(), name='event-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/$', sessionView.as_view(), name='session-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/cars/$', carsView.as_view(), name='car-list'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/(?P<number>[0-9]+)/$', carView.as_view(), name='car-detail'),
]
urlpatterns = format_suffix_patterns(urlpatterns)
api.py
class carsView(generics.ListCreateAPIView):
serializer_class = carSerializer
def get_session(self, event_id, name):
print('Getting session')
# Get event object
try:
event = Event.objects.get(event_id=event_id)
print('Found event')
except ObjectDoesNotExist:
print('Did not find event')
return
# Get session object
try:
session = event.sessions.get(name=name)
print('Found session: ', session)
return session
except ObjectDoesNotExist:
print('Did not find session')
return
def get_queryset(self):
print('Getting queryset')
print('event_id: ' + self.kwargs['event_id'])
print('name: ' + self.kwargs['name'])
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
class Meta:
model = Car
fields = (
'session',
'number',
'full_name',
'short_name',
'race_class',
'best_lap',
'best_lap_time',
'best_sector1',
'best_sector2',
'best_sector3',
'best_speed',
'pitstops',
'total_time',
'transponder',
'laps')
Solution:
This is what I actually changed to get it working.
api.py
from rest_framework.serializers import ValidationError
class carsView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
number = self.request.POST.get('number')
car = session.cars.filter(number=number)
if car.exists():
raise ValidationError('Car already exists')
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
session = serializers.StringRelatedField(required=False)
...
I see that you're creating your session ID there:
def get_queryset(self):
...
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
Then you don't need it in a serializer, only in a model. So you can set it a snot required in a serializer, but it will still be required in a model.
I guess this answer could help you: Django REST Framework serializer field required=false
I have a model BstUserActionLog with a foreign key to Django model User. I have another model for user profile information, BstUserProfile. When I do serialize BstUserActionLog with ModelSerializer I do have Django User info serialized as it is supposed to be. But I also need to add BstUserProfile serialized using the same user_id used for User model.
How can I serialize BstUserActionLog with model User and BstUserProfile are both serialized?
From my models.py:
class BstUserActionLog(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
bst_action_type = models.ForeignKey(BstActionType)
action_date = models.DateTimeField(auto_now_add=True)
bst_book = models.ForeignKey(BstBook)
new_value_id = models.IntegerField(blank=True, null=True)
old_value_id = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'bst_user_action_log'
class BstUserProfile(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, unique=True)
website = models.CharField(max_length=200)
picture = models.CharField(max_length=100)
is_avatar_uploaded = models.BooleanField(default=False)
is_cover_uploaded = models.BooleanField(default=False)
class Meta:
managed = False
db_table = 'bst_user_profile'
app_label = 'bst'
From my serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','username',)
class BstUserActionLogSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = BstUserActionLog
fields = ('id', 'user', 'bst_action_type', 'action_date', 'bst_book', 'new_value_id', 'old_value_id')
depth = 3
The key to my solution is SerializerMethodField. With this a new field can be added which is calculated with a method. This method signature contains the object to be serialized. After that a regular method serializer is used to return the serialized object.
From my serializers.py
class BstUserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = BstUserProfile
fields = ('is_avatar_uploaded', 'is_cover_uploaded')
class BstUserActionLogSerializer(serializers.ModelSerializer):
user = UserSerializer()
user_profile = serializers.SerializerMethodField()
def get_user_profile(self, obj):
try:
user_profile = BstUserProfile.objects.get(user_id=obj.user_id)
return BstUserProfileSerializer(user_profile).data
except Exception as e:
return {}
class Meta:
model = BstUserActionLog
fields = ('id', 'user', 'user_profile', 'bst_action_type', 'action_date', 'bst_book', 'new_value_id', 'old_value_id')
depth = 3
I have this simple model that acts like a rsync config that is used to pre fill in the fields for a celery periodic task. The first time i create a new rsync config trough the model everything is okay and a new periodic task is being created without a problem. When i try and alter certain fields that will change the task fields such as task arguments, I'm getting a "IntegrityError column name is not unique" I feel that it has something to do with the model save method but im not sure how to get it Right. anyone got some ideas?
here is the model:
from django.forms import ModelForm
from djcelery.models import IntervalSchedule
from djcelery.models import PeriodicTask, IntervalSchedule
INTERVAL=(
('every=5','period 5 minutes'),
)
class Customer(models.Model):
"""(Customer description)"""
customername = models.CharField(blank=True, max_length=30)
emailaddress = models.EmailField()
phonenumber = models.CharField(blank=True, max_length=10)
class Meta:
verbose_name_plural = "Customer"
def __unicode__(self):
return self.customername
class RsyncConfig(models.Model):
"""(RsyncConfig description)"""
cname = models.ForeignKey(Customer)
rsyncname = models.CharField(blank=True, max_length=255)
interval=models.CharField(max_length=8,choices=INTERVAL)
fromip = models.IPAddressField(blank=True)
source_dir = models.CharField(blank=True, max_length=255)
destination_dir = models.CharField(blank=True, max_length=255)
rsync_args = models.CharField(blank=True, max_length=255)
class Meta:
verbose_name_plural = "Rsync Config"
def __unicode__(self):
return self.cname.customername
And here is the admin.py form.
from django.contrib import admin
from django import forms
from djcelery.models import PeriodicTask, IntervalSchedule
from newrsync.models import Customer,RsyncConfig
class CustomerAdmin(admin.ModelAdmin):
class Meta:
model = Customer
class RsyncConfigAdminForm(forms.ModelForm):
list_display = ('customername', 'rsyncname','source_dir','destination_dir')
class Meta:
model = RsyncConfig
def __init__(self, *args, **kwargs):
super(RsyncConfigAdminForm,self).__init__(*args, **kwargs)
def save(self, commit=True):
interval = IntervalSchedule.objects.get(every=5,period="minutes")
model = super(RsyncConfigAdminForm, self).save(commit = False)
model.cname = self.cleaned_data['cname']
model.rsyncname = self.cleaned_data['rsyncname']
model.fromip = self.cleaned_data['fromip']
model.source_dir = self.cleaned_data['source_dir']
model.destination_dir = self.cleaned_data['destination_dir']
model.rsync_args = self.cleaned_data['rsync_args']
if commit:
model.save()
PeriodicTask.objects.get_or_create(
interval=interval,
task='apps.mftconfig.tasks.rsync_proc',
args=['rsync',
model.rsync_args,
model.source_dir,
model.destination_dir],
kwargs = {},
name = (model.cname," ",model.rsyncname),
enabled=False
)
return model
class RsyncConfigAdmin(admin.ModelAdmin):
form = RsyncConfigAdminForm
list_display = ('cname', 'rsyncname','source_dir','destination_dir')
admin.site.register(Customer,CustomerAdmin)
admin.site.register(RsyncConfig,RsyncConfigAdmin)
I basically ended up doing a delete of the object right before i save a new version.It's Not perfect but at least i circumvent the unique restrain in the PeriodicTask model and now let's hope it won't bite me in the ass later on.
If anyone has any suggestions, please! ;-)
class RsyncConfigAdminForm(forms.ModelForm):
list_display = ('customername','rsyncname','source_dir','destination_dir')
class Meta:
model = RsyncConfig
def __init__(self, *args, **kwargs):
super(RsyncConfigAdminForm,self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = super(RsyncConfigAdminForm, self).save(commit = False)
instance.customername = self.cleaned_data['customername']
instance.rsyncname = self.cleaned_data['rsyncname']
instance.fromip = self.cleaned_data['fromip']
instance.source_dir = self.cleaned_data['source_dir']
instance.destination_dir = self.cleaned_data['destination_dir']
instance.rsync_args = self.cleaned_data['rsync_args']
interval = IntervalSchedule.objects.get(every=5,period="minutes")
p=PeriodicTask.objects.filter(name=instance.rsyncname)
p.delete()
PeriodicTask.objects.get_or_create(
interval=interval,
task='apps.mftconfig.tasks.rsync_proc',
args=['rsync',
instance.rsync_args,
instance.source_dir,
instance.destination_dir],
kwargs = {},
name = (instance.rsyncname),
enabled=True
)
if commit:
instance.save()
return instance