Nested Relationship in django rest framework - python

Please help me out of this django REST framework issue .I am trying to create on rest api which which will populate data in two tables based on argument passed .
My models look like .
class WorkloadType(models.Model):
type = models.CharField(max_length=60)
status = models.BooleanField(default=True)
class Workload(models.Model):
workload_type = models.ForeignKey(WorkloadType)
workload_group = models.IntegerField(blank=True, null=True)
name = models.CharField(max_length = 60)
hdd_gb = models.IntegerField(blank=True, null=True)
ssd_gb = models.IntegerField(blank=True, null=True)
iops = models.CharField(max_length = 60)
vcpu = models.IntegerField(blank=True, null=True)
cpu = models.IntegerField(blank=True, null=True)
created_date = models.DateTimeField(auto_now_add=True, blank=True)
status = models.BooleanField(default=True)
class VdiWorkload(models.Model):
workload = models.ForeignKey(Workload)
provision_type = models.CharField(max_length = 60)
number_of_users = models.IntegerField()
json_data = models.CharField(max_length=255,blank = True,null = True)
status = models.BooleanField(default=True)
created_date = models.DateTimeField(auto_now_add=True, blank=True)
I have my serializer.py file which is looking like .
from django.forms import widgets
from rest_framework import serializers
from models import WorkloadType,Workload, \
VdiWorkload,DbWorkload,\
VmWorkload,ExchangeWorkload,RawWorkload
class VdiSerializer(serializers.Serializer):
class Meta:
model = VdiWorkload
class WorkloadSerializer(serializers.Serializer):
vdi = VdiSerializer(required = False)
pk = serializers.IntegerField(read_only=True)
workload_group = serializers.IntegerField(required=False)
workload_type = serializers.CharField(max_length = 10)
name = serializers.CharField(max_length = 60)
hdd_gb = serializers.IntegerField()
ssd_gb = serializers.IntegerField()
iops = serializers.CharField(max_length = 60)
vcpu = serializers.IntegerField()
cpu = serializers.IntegerField()
class Meta:
model = Workload
fields = ('vdi','workload_group','workload_type','name','hdd_gb','ssd_gb','iops','vcpu','cpu')
def create(self, validated_data):
"""
Create and return a new `Workload` instance, given the validated data.
"""
wt = WorkloadType.objects.get(type = validated_data['workload_type'])
validated_data['workload_type'] = wt
print "=-=-=-=-=-=-=-=-=-=-=-=-=------"
if not validated_data['workload_group']:
workload_group = Workload.objects.latest('workload_group')
validated_data['workload_group'] = workload_group
else:
try:
workload_group = Workload.objects.latest('workload_group')
validated_data['workload_group'] = workload_group + 1
except:validated_data['workload_group'] = 1
#try:node_exist = Workload.objects.get(name = validated_data['name'])
#except:node_exist = None
#if node_exist:
# raise serializers.ValidationError('Node name already exist.')
#else:
wl = Workload.objects.create(**validated_data)
VdiWorkload.objects.create(workload=wl, **validated_data)
return wl
Now I passing the rest body like.
{
"type": "Exchange",
"iops": "22",
"name": "another model",
"hdd_gb": "23",
"ssd_gb": "320",
"hdd_count": "123",
"ssd_count": "4",
"memory": "123",
"core": "114",
"rackspace": "6",
"vcpu":"12",
"workload_type":"VDI",
"workload_group":true,
"cpu":"1",
"vdi":[{
"provision_type":"user",
"number_of_users":"10",
"json_data":"any extra data which we want"
}]
}
But whenever I am making post request I am getting the error
Traceback
raceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
77. return view_func(*args, **kwargs)
File "/home/rohit/workspace/sizer/src/sizer/Workload/views.py" in workload_list
49. serializer.save()
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in save
164. self.instance = self.create(validated_data)
File "/home/rohit/workspace/sizer/src/sizer/Workload/WorkloadSerializer.py" in create
63. return Workload.objects.create(**validated_data)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py" in create
137. return self.get_query_set().create(**kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py" in create
375. obj = self.model(**kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py" in __init__
367. raise TypeError("'%s' is an invalid keyword argument for this function" % kwargs.keys()[0])
Exception Type: TypeError at /workload/workloadlist/
Exception Value: 'vdi' is an invalid keyword argument for this functi
Please do let me know if you need any other info I am still not able to solve out this question .
I am not able to identify this issue as I am new in DRF .
Please do let me know what might I am doing wrong here .My views looklike .
#csrf_exempt
def workload_list(request):
"""
List all code users, or create a new user.
"""
print "Herer I am *****************************111111"
if request.method == 'GET':
print "Herer I am *****************************"
workloads = Workload.objects.all()
serializer = WorkloadSerializer(workloads, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = WorkloadSerializer(data=data)
print serializer
print "****************** I am in ***********views now "
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)

Forgive me if this is off track but it looks to me like the model doesn't know anything about the nested field (vdi). Did you try popping it off validated_data?
Here's a small (untested) example of what I have been working on using djangorestframework==3.1.3 with Django==1.7.
models.py:
class Child(models.Model):
child_data = models.CharField(max_length=255)
class Parent(models.Model):
# Set 'related_name' to the nested field name in the serializer.
child_id = models.ForeignKey(Child, related_name='child')
serializers.py:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
class ParentSerializer(serializers.ModelSerializer):
# The ForeignKey in the model is supplied with this name so that GET
# requests can populate this and the source field below makes it
# accessible in create after validation.
child = TestInfoSerializer(source='child_id')
class Meta:
model = Parent
fields = ('child', )
def create(self, validated_data):
child_data = validated_data.pop('child_id') # strip out child_id for subsequent Parent create
try:
# try to find an existing row to fulfill the foreign key
child_instance = Child.objects.filter(**child_data)[0]
except IndexError:
# create a new row
child_instance = Child.objects.create(**child_data)
return Parent.objects.create(child_id=child_instance, **validated_data)
With this I can POST nested JSON without thinking about the foreign key:
{
"child": {
"child_data": "asdf"
}
}
GET also returns the nested representation with this setup.
I hope this helps.

Related

How can I send a post request in Django Rest Framework where a key has list of json objects?

I am new to Django Rest Framework and django itself. I want to post a request wherein a key has a list of objects. So far, I have created the User_parameter model and I am passing it as a foreign key. I am lost and do not know what to do from here on. Do I need to override create() in the User_Parameters_Serializerserializer or something entirely different?
I have added code for my models.py, serializer.py, the function-based view where I am handling the post request and my desired post request outline.
Thank you for your time. I really appreciate it.
# The post request I want to send looks like this:
{
"model_path": "some path",
"data_path": "some path for csv",
"sep": ",",
"target_column": "target_column",
"label_column": "lable_column",
"function_names": "some strings",
"user_params": [{
"standard_deviation": between[1-3],
"direction": 0 OR 1,
"feature_list": "some strings"
}]
}
#models.py
class User_Parameters(models.Model):
def validate_standard_deviation_number(value):
if value not in range(1,4):
raise ValidationError(
_('%(value)s is not in range: [1-3]'),
params={'standard deviation': value},
)
def validate_direction(value):
#### 0 - Negative
#### 1 - Positive
if value not in range(0,2):
raise ValidationError(
_('%(value)s needs to be either 0 (Negative) or 1 (Positive)'),
params={'direction': value},
)
standard_deviation = models.IntegerField(default=1, validators=[validate_standard_deviation_number])
direction = models.IntegerField(default=1, validators=[validate_direction])
feature_list = models.CharField(default='', max_length=3000)
def __str__(self):
return "std dev: %s, dir: %s, feature list: %s" % (self.standard_deviation, self.direction, self.feature_list)
class Machine_Learning_Test_Engine(models.Model):
model_path = models.CharField(default='', max_length = 3000)
data_path = models.CharField(default='', max_length = 3000)
sep = models.CharField(default='', max_length = 3000)
target_column = models.CharField(default='', max_length = 3000)
label_column = models.CharField(default='', max_length = 3000)
function_names = models.CharField(default='', max_length=3000)
user_params = models.ForeignKey(User_Parameters, on_delete=models.CASCADE)
#serializer.py
class User_Parameters_Serializer(serializers.ModelSerializer):
class Meta:
model = User_Parameters
fields = '__all__'
class MLTE_Serializers_Model(serializers.ModelSerializer):
class Meta:
model = Machine_Learning_Test_Engine
fields = '__all__'
#api_view(['POST'])
def mlte_reqeust(request):
if request.method == 'POST':
serializer = MLTE_Serializers_Model(data = request.data)
if serializer.is_valid():
serializer.save()
global mlte
mlte = MLTE(serializer.data['model_path'], serializer.data['data_path'],serializer.data['sep'], serializer.data['target_column'],serializer.data['label_column'])
functions = str(serializer.data['function_names']).split(",")
### I have to pass the ```{"user_param":[{A1}]}``` as the argument of ```generate_data()```.
mlte.generate_data(functions)
json_data_pandas = pandas.DataFrame.to_json(mlte.data.head())
return Response(json_data_pandas, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Dynamically alter form fields in views.py file of django

I was wondering if there is a way that I can alter a model form within the views.py file to create a multiple choice dropdown field for form choices. I want to set each option on the choice field from the results of a queryset.
for example:
I want to from_acct field to have a scroll down option with the following list..
wells fargo
chase
tabz
bank of america
the list of banks are results of a query set
Here is what i have so far in the views.py file.
form = TransferForm()
form.fields['from_acct'].queryset = Accounts.objects.filter(user = currentUser).all()
message = 'please fill out the below form'
parameters = {
'form':form,
'currentUser':currentUser,
'message':message,
}
return render(request, 'tabs/user_balance.html', parameters)
here is the forms.py file
class TransferForm(forms.ModelForm):
class Meta:
model = Transfers
fields = ['from_acct', 'to_acct', 'amount', 'memo']
labels = {
'from_acct':'from',
'to_acct':'to',
}
here is the model.py file
class Transfers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
from_acct = models.CharField(max_length=150, default='account')
to_acct = models.CharField(max_length=150, default='accont')
amount = models.DecimalField(decimal_places=2, max_digits=9, default=0)
memo = models.CharField(max_length=200, default='memo')
frequency = models.SmallIntegerField(default=1)
status = models.SmallIntegerField(default=1)
create = models.DateTimeField(auto_now_add=True)
You can try to set choices arg for CharField by function.
Like that:
class Transfers(models.Model):
field = models.CharField(max_length=255, choices=result_query())
def result_query(self):
# you can use that with self if u need transfers.pk for querying
return Something.objects.exclude(bank_id__in=[bank.id for bank in self.banks.all())
def result_query():
# or there if not
return Something.objects.filter(any_field__gte=123)
For sure, you can realize any logic in the function, so you can dynamically change options for char field.
UPDATE:
Sure, u haven't pass request into the function.
That should be like that:
view.py:
def my_view(request):
if request.method == 'GET':
form = TransferForm(user=request.user)
...
return something here
forms.py
class TransferForm(ModelForm):
class Meta:
model = Transfer
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(TransferForm, self).__init__(*args, **kwargs)
self.fields['accounts'].choices = Accounts.objects.filter(user = currentUser).all()

How to map foreign key when serializing POST data?

I'm trying to map a foreign key to POST data when creating a new object through a serializer. There are two foreign keys in the object, one is serializing perfectly, the other is creating an error.
Model:
class Event(models.Model):
owner = models.ForeignKey('auth.User', related_name='owner', blank=True)
date = models.DateField('eventdate')
time = models.TimeField('eventtime', default=now)
eventtype = models.ForeignKey(EventType, related_name='eventtype', blank=True)
# duration = models.DurationField()
location = models.CharField(max_length=200, blank=True)
attenders = models.ManyToManyField(User, related_name='attenders')
invited = models.ManyToManyField(User, related_name='invitedlist')
View:
class EventMixin(RetrieveUpdateDestroyAPIView, CreateAPIView):
serializer_class = EventSerializer
def get_queryset(self):
return Event.objects.all()
def partial_update(self, request, *args, **kwargs):
request['owner'] = request.user
sname = request['eventtype']
request['eventtype'] = EventType.objects.filter(sname=sname)
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
return super(EventMixin, self).partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
new = {}
new['owner'] = request.user.__dict__
new['date'] = request.data['date']
new['time'] = request.data['time']
new['location'] = request.data['location']
sname = request.data['eventtype']
new['eventtype'] = EventType.objects.get(sname=sname).__dict__
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
serializer = EventMixinSerializer(data=new)
with open('/tmp/log.txt', 'w+') as f:
f.write(str(serializer.is_valid()))
f.write(str(serializer.validated_data))
f.close()
serializer.is_valid();
serializer.save()
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
Serializer:
class EventMixinSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
eventtype = EventTypeSerializer()
attenders = FriendsListingField(read_only=True)
invited = FriendsListingField(read_only=True)
class Meta:
model = Event
fields = ('owner', 'eventtype', 'date', 'time', 'location', 'id', 'attenders', 'invited')
def create(self, validated_data):
owner = validated_data.pop('owner')
owner = owner.instance
eventtype = validated_data.pop('eventtype')
eventtype = eventtype.instance
event = Event.objects.create(owner=owner, eventtype=eventtype, **validated_data)
event.save()
return event
Error when owner field present:
False
{'owner': OrderedDict([('username', ['A user with that username already exists.'])])}
Result when UserSerializer(read_only=True) (pretty much diabling it):
True
OrderedDict([('eventtype', OrderedDict([('lname', 'Swimming'), ('sname', 'SWM'), ('category', '1')])), ('date', datetime.date(2015, 12, 22)), ('$
(Notice the event type data in the result)
Thanks!
You need to remove the validators from UserSerializer.
Assuming UserSerializer is a User ModelSerializer it'll extract the unique constraint on the User.username from the Model and your validation will fail.
To work this around you'll need to remove the UniqueValidator by overriding the validators list for the username field of the UserSerializer

DjangoModelPermission with multiple databases

I have 2 databases. One containing the AUTH that's also extended in the following models.py -
from django.contrib.auth.models import User
class FileIndex(models.Model):
filename = models.CharField(max_length=256)
filetype = models.CharField(max_length=16)
vendorid = models.IntegerField()
vendorname = models.CharField(max_length=256)
tablename = models.CharField(max_length=256)
class Meta:
db_table = 'file_index'
verbose_name = 'File/Vendor Index'
verbose_name_plural = 'File/Vendor Indicies'
def __str__(self):
return self.filename
class UserFile(models.Model):
userid = models.ForeignKey(User)
fileid = models.ForeignKey(FileIndex)
grant_date = models.DateTimeField()
revoke_date = models.DateTimeField(blank=True)
class Meta:
db_table = 'auth_files'
verbose_name = 'User File Matrix'
verbose_name_plural = 'User File Matricies'
the 'tablename' field in FileIndex references a Table Name in another database referenced in a separate App. My current test view I'm using is follows in my views.py
class File_List(generics.ListAPIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
filename = self.request.GET.get('filename')
model = get_model('markit', filename)
filedate = self.request.GET.get('filedate')
queryset = model.objects.using('markitdb').filter(Date__contains=filedate)
return queryset
If I'm not logged in it works fine and gives "not authorized" but regardless of if I've set view permissions on the table or not my user can still execute the view.
Model is listed before the function or else it will complain about the model not being there. I want to figure that out later. First I'm trying to understand why my view is still executing even if the user does not have group permission to view the Model.
I've attacked this by doing the following in my view -
class ExampleView(APIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get(self, request, format=None):
if UserFile.objects.filter(fileid_id=1, userid_id=2).exists():
content = {
'status': 'Request Successful.'
}
return Response(content)
else:
content = {
'status': 'Request Failed.'
}
return Response(content)
Essentially after authentication it's doing a query against the UserFile to validate the User and the file exists and if it does then I can write it to do the queryset or not.

Explanation on Django's OneToOneField, ManyToManyField and ManyToOneField

I am a bit puzzled with the Django object models. I have models like this:
# Create your models here.
class Item(models.Model):
code = models.CharField(max_length=200, unique=True)
barcode = models.CharField(max_length=300)
desc = models.CharField('Description',max_length=500)
reg_date = models.DateField('registered date')
registrar = models.CharField(max_length=100)
def __unicode__(self):
return self.code + ' : ' + self.desc
class ItemInfo(models.Model):
item = models.OneToOneField(Item, primary_key=True)
supplier = models.ForeignKey(Supplier)
stock_on_hand = models.IntegerField()
stock_on_order = models.IntegerField()
cost = models.IntegerField()
price = models.IntegerField()
unit = models.CharField(max_length=100)
lead_time = models.IntegerField()
But when I tried to retrieve the Item and ItemInfo into modelforms, I got this error:
'ModelFormOptions' object has no attribute 'many_to_many'. I suspected there is something wrong with this line supplier = models.ForeignKey(Supplier). Can someone explained me when to use ForeignKeyor the relationships fields (OneToOneFields, ManyToManyFields, ManyToOneFields)
Edit: ModelForm:
class ItemForm(ModelForm):
class Meta:
model = Item
widgets = {
'registrar' : TextInput(attrs={'ReadOnly' : 'True'})
}
class ItemInfoForm(ModelForm):
class Meta:
model = ItemInfo
exclude = ('item')
This is how I generate the form with populated value from the models:
def details(request, code):
csrf_context = RequestContext(request)
current_user = User
if request.user.is_authenticated():
item = Item.objects.get(pk=code)
item_info = ItemInfo.objects.get(pk=item.pk)
item_form = ItemForm(instance=item)
item_info_form = ItemInfoForm(instance=item_form)
return render_to_response('item/details.html',
{'item_form' : item_form, 'item_info_form' : item_info_form},
csrf_context)
else:
return render_to_response('error/requires_login.html', csrf_context)
Traceback:
Traceback:
File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "G:\tulip\stock\item\views.py" in details
131. item_info_form = ItemInfoForm(instance=item_form)
File "C:\Python27\lib\site-packages\django\forms\models.py" in __init__
237. object_data = model_to_dict(instance, opts.fields, opts.exclude)
File "C:\Python27\lib\site-packages\django\forms\models.py" in model_to_dict
112. for f in opts.fields + opts.many_to_many:
Exception Type: AttributeError at /item/details/1/
Exception Value: 'ModelFormOptions' object has no attribute 'many_to_many'
You are instantiating ItemInfoForm with ItemForm instance. While instance should be ItemInfo instance, not form
Correct line:
item_info_form = ItemInfoForm(instance=item_info)

Categories