I'm adding a 'tests' field to my 'Sample' model, where 'tests' will be a list of 'TestRequest' objects. Currently, I'm getting this error:
Got AttributeError when attempting to get a value for field `tests` on serializer `SampleSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Sample` instance.
Original exception text was: 'Sample' object has no attribute 'tests'.
'tests' is not a field on my model. I'm just trying to add it to the serialized data. Currently, I can get a nested serializer to work for 'klass' but that is because it's defined in the model.
Models:
class Sample(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
comments = models.TextField(null=True, blank=True)
klass = models.ForeignKey('samples.Batch', null=True, blank=True,
related_name='samples', verbose_name='Batch')
product = models.ForeignKey('customers.Product', blank=False)
NOTRECEIVED = 'nr'
RECEIVED = 'rc'
DISCARDED = 'dc'
DEPLETED = 'dp'
SAMPLE_STATUS = (
(NOTRECEIVED, 'not received'),
(RECEIVED, 'received'),
(DISCARDED, 'discarded'),
(DEPLETED, 'depleted'),
)
status = models.CharField(
max_length=2, choices=SAMPLE_STATUS, default=NOTRECEIVED)
is_recycling = models.BooleanField(default=False)
is_submitted = models.BooleanField(default=False)
received_date = models.DateTimeField(
_('date received'), null=True, blank=True)
class TestRequest(models.Model):
client = models.ForeignKey('customers.Client')
company = models.ForeignKey('customers.Company')
sample = models.ForeignKey('samples.Sample')
procedure_version = models.ForeignKey('registery.ProcedureVersion')
replicates = models.PositiveIntegerField(editable=True, null=True, blank=True)
created_date = models.DateTimeField('Date created', auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
comments = models.TextField('Comments', blank=True)
Serializers:
class TestSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(lookup_field='pk', lookup_url_kwarg='pk', read_only=True, view_name='samples_api:test-detail')
class Meta:
model = TestRequest
fields = ('id', 'href',)
class SampleBatchSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(
lookup_field='pk', lookup_url_kwarg='batch_pk', read_only=True, view_name='samples_api:batch-detail')
class Meta:
model = Batch
fields = ('id', 'href',)
class SampleSerializer(serializers.ModelSerializer):
tests = TestSerializer(many=True)
klass = SampleBatchSerializer(many=False)
class Meta:
model = Sample
# list_serializer_class = FilteredListSerializer
fields = ('id', 'name', 'tests', 'klass',)
def create(self, validated_data):
...
def update(self, instance, validated_data):
...
Viewsets:
class TestRequestViewSet(viewsets.ModelViewSet):
"""
Viewset for the TestRequest model
"""
serializer_class = TestRequestSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return TestRequest.objects.filter(company=client.company)
def perform_create(self, serializer):
# Override default creatation to provide request based information.
client = get_object_or_404(Client, user=self.request.user)
company = client.company
serializer.save(client=client, company=company)
class SampleViewSet(viewsets.ModelViewSet):
"""
Viewset for the Sample model
"""
serializer_class = SampleSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return Sample.objects.filter(klass__company=client.company)
I would rather not have to add the field to the model. A 'Sample' can have many 'TestRequest's but a 'TestRequest' can only have one 'Sample'.
How do I get my serializer to add the 'tests' field that isn't in the model?
in your SampleSerializer. You have specified 'tests' which is not in your Sample class in your model...
Use nested SerializerMethodField as below....
tests = serializers.SerializerMethodField()
def get_tests(self, obj):
var=TestRequestSerializer(obj.id)
return var.data
Related
I am trying to add Value into InstantInvestment Model in Django REST Framework which is working. but, only want to show the shipping that is specifically for the login user in. which means, the present situation is giving all the shipping not for this user.
models.py
class Shipping(models.Model):
investor = models.ForeignKey(User, related_name='shipping', on_delete=models.CASCADE)
beneficiary = models.CharField("Beneficiary Name", max_length=150)
bank = models.ForeignKey(Bank, related_name="bank", on_delete=models.CASCADE)
account = models.CharField(max_length=10)
address = models.TextField("Shipping Adresss")
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.beneficiary
class Meta:
verbose_name = 'Shipping'
verbose_name_plural = 'Shippings'
class InstantInvestment(models.Model):
investor = models.ForeignKey(User, related_name='instantivestment', on_delete=models.CASCADE)
ref_code = models.CharField(max_length=200, blank=True, null=True)
investment = models.FloatField("Investment in dollar")
rate = models.FloatField("Exchange Rate")
transferable = models.FloatField("Money Transferable")
conversion = models.FloatField("Rate in Naira")
product = models.ForeignKey(Product, related_name='instant_product', on_delete=models.CASCADE)
shipping = models.ForeignKey(Shipping, related_name='shipping', on_delete=models.CASCADE)
done = models.BooleanField("Completed Transaction", default=False)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.investor.get_full_name()} - Transaction Code: {self.ref_code}'
class Meta:
verbose_name = 'InstantInvestment'
verbose_name_plural = 'InstantInvestments'
serializers.py
class ShippingSerializer(serializers.ModelSerializer):
class Meta:
model = Shipping
fields = ('beneficiary', 'bank', 'account', 'address')
class QucikPaymentSerializer(serializers.ModelSerializer):
class Meta:
model = InstantInvestment
fields = ('url', 'id','investment', 'rate', 'transferable', 'conversion', 'product', 'shipping')
views.py
class QuickPaymentView(viewsets.ModelViewSet):
queryset = InstantInvestment.objects.all()
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
Remove the query set attribute in your viewset class
class QuickPaymentView(viewsets.ModelViewSet):
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
to make it work you need to specify the basename key word argument when you register your viewset class with router.
router.register(r'quickpayment/' , QuickPaymentView , basename='InstantInvestment')
If you want do so you need to write a html separately. I think that you showed in your question is rest frameworks ui to test the api. That UI can't determine the User before you send the request.
I have problem with Django restframe work i have 2 table that one of them is a foreign key to another i have used RelatedField in serializer but i get an error:'Relational field must provide a queryset argument,
can someone help me in this case
my code is as below:
class DocTable(models.Model):
project = models.CharField(max_length=1000, null=True, blank=True)
document_no = models.CharField(max_length=1000, null=True, blank=True)
document_title = models.TextField(null=True, default='', blank=True)
class PlanTable(models.Model):
document = models.ForeignKey(DocTable, on_delete=models.CASCADE, related_name='doctable')
work_type = models.CharField(max_length=1000, null=True, blank=True)
description_work = models.TextField(null=True, default='', blank=True)
serializers.py
class DocTableSerializer(serializers.ModelSerializer):
doctable = serializers.RelatedField(many=True)
class Meta:
model = DocTable
fields = ['pk', 'project', 'document_no', 'doctable']
read_only_fields = ['pk']
class PlanTableSerializer(serializers.ModelSerializer):
class Meta:
model = PlanTable
fields = ['pk', 'document', 'work_type', 'description_work']
read_only_fields = ['pk']
views.py
class DocTableListView(generics.ListAPIView):
lookup_field = 'pk'
serializer_class = DocTableSerializer
def get_queryset(self):
return PlanTable.objects.all()
def get_object(self):
pk = self.kwargs.get('pk')
return PlanTable.objects.get(pk=pk)
You have to provide queryset in RelatedField like this.
class DocTableSerializer(serializers.ModelSerializer):
doctable = serializers.RelatedField(many=True, queryset=DocTable.objects.all())
Or if you only want to use this related field for retrieving data, you can mark it as read only
doctable = serializers.RelatedField(many=True, read_only=True)
I have a question in django rest framework. Since I'm learning how to use some advanced options, I do not quite understand. I need to currently change a booleanfield every time a foreignkey is inserted into table.
How can I do this in model ?
Model:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
class Credit(models.Model):
number = models.CharField(max_length=16, unique=True)
create_at = models.DateField(auto_add_now=True)
updated_at = models.DateField()
available = models.BooleanField()
def __str__(self):
return self.number
class Meta:
db_table = 'credit'
app_label = 'bank'
Serializer:
class PersonaSerializer(serializers.ModelSerializer):
order__id = serializers.ReadOnlyField(source='order.id')
class Meta:
model = Persona
fields = '__all__'
class Persona(viewsets.ModelViewSet):
allowed_methods = ('GET', 'POST', 'PUT', 'PATCH')
queryset = Persona.objects.all()
serializer_class = PersonaSerializer
You can override the create method on the ModelSerializer to achieve this:
def create(self, validated_data):
cart = validated_data['cart']
persona = super(PersonaSerializer, self).create(validated_data)
cart.available = True # update boolean
cart.save()
return persona
You can read more about this in the docs
If you want to handle this in your model you can override the Persona model save method:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
def save(self, *args, **kwargs):
# Will only update the available field when the Persona
# instance is created and the cart field is not null
if not self.pk and self.cart:
self.cart.available = True
self.cart.save()
return super(Persona, self).save(*args, **kwargs)
At the moment I developed the following code, for me to get the Contact List of each user. The views return the ID numbers of the Contacts of the User. I need to get, instead of the ID numbers, the 'name' and 'last_name' attribute of said contacts. I am quite new to Django's REST Framework and I'm not quite sure what to do next but I believe I have to nest the APIView. I would really appreciate some help!
views.py
def list_contacts(request, id_profile):
url = request.build_absolute_uri(reverse('api_users:contact_list', kwargs={'pk':id_profile}))
response = requests.get(url)
profile = Profile.objects.get(pk=id_profile)
if response.status_code == status.HTTP_200_OK:
data = response.content
user = json.loads(data)
return render(request, 'profiles/contact_list.html', {'user':user})
models.py
class Profile(models.Model):
id_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birthday = models.DateField(auto_now=False)
description = models.CharField(max_length=100)
profile_picture = models.ImageField(upload_to='images/profiles/%Y/%m/%d', blank=False)
active = models.BooleanField(default = False)
contacts = models.ManyToManyField('self', blank=True, default='null')
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
deleted_at = models.DateTimeField(blank=True, null=True)
class Meta:
ordering = ('-id',)
def __str__(self):
return self.name+' '+self.last_name
def active_profiles():
return Profile.objects.filter(active=True)
api/views.py
class ContactListView(generics.ListAPIView):
queryset = Profile.objects.all()
serializer_class = UserContactListSerializer
filter_backends = (filters.SearchFilter,)
search_fields = ('name', 'last_name',)
def get(self, request, pk, format=None):
contacts = Profile.objects.get(pk=pk)
serializer = UserContactListSerializer(contacts)
return Response(serializer.data)
api/serializers.py
class UserContactListSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['name','last_name','contacts']
I don't know what exactly is going on in your list_contacts but if you want to use the same serializer as a field in itself, you currently can't.
While Django models allow you to use 'self' as the reference, DRF doesn't.
What you can instead do is create another serializer and use that as the field.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name")
class UserContactListSerializer(serializers.ModelSerializer):
contacts = UserSerializer(many=True)
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "contacts")
so I have slightly complex database structure with quite a few m2m relations. I am facing this issue with some of the viewsets that are defined on a few tables structured quite similarly. Following are some example models.
# Boards model
class Board(models.Model):
id = models.CharField(max_length=10, primary_key=True)
board = models.CharField(max_length=40, unique=True)
class Meta:
verbose_name_plural = 'Boards'
def __unicode__(self):
return self.board
# Grades model
class Grade(models.Model):
id = models.CharField(max_length=10, primary_key=True)
grade = models.CharField(max_length=40, unique=True)
class Meta:
verbose_name_plural = 'Grades'
def __unicode__(self):
return self.grade
# BoardGrades model
class BoardGrade(models.Model):
board = models.ForeignKey(to=Board)
grade = models.ForeignKey(to=Grade)
class Meta:
verbose_name_plural = 'BoardGrades'
unique_together = ('board', 'grade')
def __unicode__(self):
return '%s - %s' % (self.board.board, self.grade.grade)
# Subjects model
class Subject(models.Model):
id = models.CharField(max_length=5, primary_key=True)
name = models.CharField(max_length=40, unique=True, blank=False, null=False)
board_grade = models.ManyToManyField(to='lguser.BoardGrade')
def_icon = models.ImageField(upload_to='subject_icons', null=True, blank=True)
sel_icon = models.ImageField(upload_to='subject_icons', null=True, blank=True)
class Meta:
verbose_name_plural = 'Subjects'
def __unicode__(self):
return self.name
# Units model
class Unit(models.Model):
name = models.CharField(max_length=100, unique=True, blank=False, null=False)
subject = models.ForeignKey(to=Subject, blank=False, null=False, related_name='units')
board_grade = models.ManyToManyField(to='lguser.BoardGrade')
icon = models.ImageField(upload_to='unit_icons', null=True, blank=True)
class Meta:
verbose_name_plural = 'Units'
def __unicode__(self):
return '%s - %s' % (self.subject.name, self.name)
Following are the serializer classes I wrote for the the Subject and Unit model
class SubjectSerializer(serializers.HyperlinkedModelSerializer):
board_grade = serializers.PrimaryKeyRelatedField(queryset=BoardGrade.objects.all(), many=True)
class Meta:
model = Subject
fields = ('name', 'board_grade', 'def_icon', 'sel_icon', 'id', 'url')
class UnitSerializer(serializers.PrimaryKeyRelatedField):
board_grade = serializers.PrimaryKeyRelatedField(queryset=BoardGrade.objects.all(), many=True)
subject = serializers.PrimaryKeyRelatedField(queryset=Subject.objects.all())
class Meta:
model = Unit
fields = ('name', 'subject', 'board_grade', 'icon', 'id', 'url')
And following are the viewsets defined for the above two serializers
class SubjectViewSet(viewsets.ModelViewSet):
authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication, BasicAuthentication]
permission_classes = [permissions.IsAuthenticated, permissions.IsAdminUser]
queryset = Subject.objects.all()
serializer_class = SubjectSerializer
class UnitViewSet(viewsets.ModelViewSet):
authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication, BasicAuthentication]
permission_classes = [permissions.IsAuthenticated, permissions.IsAdminUser]
queryset = Unit.objects.all()
serializer_class = UnitSerializer
and finally the urls:
router = DefaultRouter()
router.register(r'subject', SubjectViewSet)
router.register(r'unit', UnitViewSet)
urlpatterns = router.urls
when I hit the /subject/ url with a GET requests it gives me the JSON array as desired. but when I hit the /unit/ url with the request it throws an error as follows.
TypeError at /lgadmin/unit/
__init__() takes exactly 1 argument (3 given)
Request Method: GET
Request URL: http://127.0.0.1:8000/lgadmin/unit/
Django Version: 1.8.4
Exception Type: TypeError
Exception Value:
__init__() takes exactly 1 argument (3 given)
I am unable to see where the issue is. I used the prebuilt ModelViewSet in as vanilla a fashion as I could. Any help??
Looking in your code class UnitSerializer(serializers.PrimaryKeyRelatedField): that is invalid.
Going by the docs there you would have to use one of the following -
serializers.ModelSerializer
serializers.HyperlinkedModelSerializer
serializers.ListSerializer
serializers.BaseSerializer
You're trying to pass a field serializer into a serializer "view".