I'm learning to write an API using tastypie for a Django project. I have following code in models.py:
from django.db import models
from django.contrib.auth.models import User
class UserDeviceIds(models.Model):
user = models.ForeignKey(User)
device_id = models.CharField(max_length=15)
def __str__(self):
return self.device_id
class UserProfile(models.Model):
user = models.OneToOneField(User)
referral_code = models.CharField(max_length=8, unique=True)
def __str__(self):
return str(self.mal_rank)
And, I have following code in api.py:
class CreateUserProfileResource(ModelResource):
user = fields.ForeignKey('users.api.CreateUserResource', 'user', full=True)
class Meta:
list_allowed_methods = ['get','post']
always_return_data = True
authorization = Authorization()
authentication = Authentication()
validation = CreateUserProfileValidation()
resource_name = 'auth'
queryset = UserProfile.objects.all()
def hydrate(self, bundle):
bundle.data["user"]['username'] = bundle.data.get('country_code') + bundle.data.get("user")['username']
# Set a password automatically:
raw_password = ''.join(random.choice(string.ascii_lowercase) for i in range(8))
u = User(username='dummy')
u.set_password(raw_password)
bundle.data["user"]['password'] = u.password
return bundle
class CreateUserResource(ModelResource):
class Meta:
authorization = Authorization()
authentication = Authentication()
always_return_data = True
resource_name = 'user'
queryset = User.objects.all()
excludes = ['is_active', 'is_staff', 'is_superuser', 'date_joined', 'last_login']
When I send a POST request to http://127.0.0.1:8000/api/v1/auth (i.e:
curl -X POST -H "Content-Type: application/json" -d '{"user": {"email":"a#b.com","username":"abcdef"}, "referral_code":"abc123"}' http://127.0.0.1:8000/api/v1/auth
then a User and a UserProfile object is successfully created. But I would also like to create a UserDeviceIds object in the same endpoint. I have tried combination of different Tastypie Relationship Fields but I can't create a UserDeviceIds object. Can someone please elaborate on Tastypie relationship fields with some sample code to make me understand how the relationships work in Django?
For instance, I edited my CreateUserProfileResource in api.py and added the following line:
deviceid = fields.ForeignKey('users.api.CreateUserDeviceIdsResource', 'deviceid', full=True)
so that CreateUserProfileResource now looks like:
class CreateUserProfileResource(ModelResource):
user = fields.ForeignKey('users.api.CreateUserResource', 'user', full=True)
deviceid = fields.ForeignKey('users.api.CreateUserDeviceIdsResource', 'deviceid', full=True)
class Meta:
list_allowed_methods = ['get','post']
...
...
and added a new Resource for UserDeviceIds model:
class CreateUserDeviceIdsResource(ModelResource):
class Meta:
authorization = Authorization()
authentication = Authentication()
always_return_data = True
resource_name = 'deviceid'
queryset = UserDeviceIds.objects.all()
and when I try to send a POST request:
curl -X POST -H "Content-Type: application/json" -d '{"user": {"email":"a#b.com","username":"abcdef"}, "referral_code":"abc123", "deviceid": {"device_id": "abc"}}' http://127.0.0.1:8000/api/v1/auth
I get following error traceback:
{"error_message": "null value in column \"user_id\" violates not-null constraint
DETAIL: Failing row contains (2, abc, def, ghi, null).
", "traceback": "Traceback (most recent call last):
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
return self.cursor.execute(sql, params)
psycopg2.IntegrityError: null value in column \"user_id\" violates not-null constraint
DETAIL: Failing row contains (2, abc, def, ghi, null).
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 201, in wrapper
response = callback(request, *args, **kwargs)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 432, in dispatch_list
return self.dispatch('list', request, **kwargs)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 464, in dispatch
response = method(request, **kwargs)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 1340, in post_list
updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2104, in obj_create
return self.save(bundle)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2247, in save
self.save_related(bundle)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2318, in save_related
related_resource.save(related_bundle)
File \"/vagrant/venv/lib/python3.4/site-packages/tastypie/resources.py\", line 2250, in save
bundle.obj.save()
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 589, in save
force_update=force_update, update_fields=update_fields)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 617, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 698, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/base.py\", line 731, in _do_insert
using=using, raw=raw)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/manager.py\", line 92, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/query.py\", line 921, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py\", line 920, in execute_sql
cursor.execute(sql, params)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 81, in execute
return super(CursorDebugWrapper, self).execute(sql, params)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
return self.cursor.execute(sql, params)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/utils.py\", line 94, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File \"/vagrant/venv/lib/python3.4/site-packages/django/utils/six.py\", line 658, in reraise
raise value.with_traceback(tb)
File \"/vagrant/venv/lib/python3.4/site-packages/django/db/backends/utils.py\", line 65, in execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: null value in column \"user_id\" violates not-null constraint
DETAIL: Failing row contains (2, abc, def, ghi, null).
"}
which I understand means that when it tried to save a UserDeviceIds object, it could not find data in bundle to put in the ForeignKey(User) field of UserDeviceIds model.
Can someone explain what needs to be done to make this code work?
Thanks!
You can't achieve it in current relation. The model resources are true reflection of models. For instance you could create this:
>>> UserProfile.objects.create(refferal_code='asdfasdf', user=User.objects.create_user(username='asdfasdf'))
or this:
>>> UserDeviceIds.objects.create(device_id='adfadf', user=User.objects.create_user(username='asdfasdfas'))
But not both in one instruction.
The REST API is great because it quite well feet Django ORM own interface.
So you can create object with children but not with other parents at the same time.
You have named your Resources with functional purpose creation that clearly suggest something is not right. Your Resource should be assign only to model.
In my own opinion REST API is great for working with, because it's clear, easy to understand for clients, easy rules, fast to build, easy to test. But doesn't feet really well sign-up with Django.
If you will have to break some RESTful rules don't try to redefine existing fully functioning Resources it can give a lot of pain. Instead Make some custom separated endpoint well described for this purpose.
I suggest to create custom view like endpoint handling json/xml whatever you support for sign-up. Hold it in separated app if you find better solution in future you will easily replace it. I suggest it because: 1. It's critical for you to create these 3 models in one call, it will break models if internet gets off. 2. Will be faster.
I doubt in existence of the ready apps solving the problem because sing-up is always bit unique for each website.
import json
from django.contrib.auth.models import User
from django.http import (HttpResponse, HttpResponseBadRequest,
HttpResponseNotAllowed)
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def signup(request):
"""
Description:
POST faldksfalskdfjalksdf
Headers:
sdfasdf
Attributes:
sadljfkasdlfksad
Responses:
asdf
"""
if request.method != 'POST':
return HttpResponseNotAllowed(permitted_methods=('POST',))
try:
data = json.loads(request.body)
except (AttributeError, ValueError):
return HttpResponseBadRequest(
json.dumps({'error': 'JSON format invalid',
'success': False}),
content_type='application/json')
if data.get('username', None) and data.get('device_id', None) and data.get('country', None):
# add more validation.
user = User.objects.create_user(username=data['username'])
UserProfile.objects.create(refferal_code='asdfasdf', user=user)
UserDeviceIds.objects.create(device_id='adfadf', user=user)
return HttpResponse(json.dumps({'success': True}),
content_type='application/json')
else:
return HttpResponseBadRequest(
json.dumps({'error': 'username, device_id or country attributes missing.',
'success': False}),
content_type='application/json')
Related
I am a Django beginner and a SOF newbie, sorry if this question sounds a silly to some.
I am struggling with my integration tests.
In my app I have a one-to-one User/Profile relationship. I have a list view to show registered users' profile data:
class ProfileListView(views.ListView, LoginRequiredMixin):
model = Profile
template_name = 'registration/profile_list.html'
paginate_by = 8
def get_context_data(self, *, object_list=None, **kwargs):
context = super(ProfileListView, self).get_context_data()
# superuser raises DoesNotExist at /accounts/profiles/ as they are created with createsuperuser in manage.py
# hence are not assigned a profile automatically => create profile for them here
try:
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
except ObjectDoesNotExist:
Profile.objects.create(user=self.request.user)
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
# get all other users' profiles apart from staff and current user
regular_users = User.objects \
.filter(is_superuser=False, is_staff=False, is_active=True) \
.exclude(pk=self.request.user.pk)
context['non_staff_active_profiles'] = Profile.objects.filter(user__in=regular_users)
return context
I want to test the get_context_data() method to ensure it returns:
correct logged in user
correct queryset of non-staff profiles
My test breaks as soon as I try something like:
response = self.client.get('/accounts/profiles/')
I understand I need to pass user/profile data to the client but I could not figure out how to do that. It looks like it fails because of context['current_profile'] = Profile.objects.get(pk=self.request.user.pk) and I have no idea why.
The whole 'test' is below:
def test_view_get_context_data__should_return_correct_context(self):
new_user = User.objects.create_user(**self.VALID_USER_DATA_1)
# create profile
new_profile = Profile.objects.create(user=new_user)
# test profile
self.assertEqual(new_profile.user_id, new_user.pk)
response = self.client.get('/accounts/profiles/')
It fails with:
/home/kk/Documents/Github/Phonotheque/venv/bin/python /snap/pycharm-professional/280/plugins/python/helpers/pycharm/django_test_manage.py test Phonotheque.accounts_app.tests.views.test_ProfileListView.ProfilesListViewTests.test_view_get_context_data__should_return_correct_context /home/kk/Documents/Github/Phonotheque
Testing started at 15:54 ...
Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/list.py:91: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'Phonotheque.accounts_app.models.Profile'> QuerySet.
return self.paginator_class(
Destroying test database for alias 'default'...
Error
Traceback (most recent call last):
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/views.py", line 138, in get_context_data
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/query.py", line 496, in get
raise self.model.DoesNotExist(
Phonotheque.accounts_app.models.Profile.DoesNotExist: Profile matching query does not exist.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/tests/views/test_ProfileListView.py", line 65, in test_view_get_context_data__should_return_correct_context
response = self.client.get('/accounts/profiles/')
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 836, in get
response = super().get(path, data=data, secure=secure, **extra)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 424, in get
return self.generic(
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 541, in generic
return self.request(**r)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 810, in request
self.check_exception(response)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 663, in check_exception
raise exc_value
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 84, in view
return self.dispatch(request, *args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 119, in dispatch
return handler(request, *args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/list.py", line 174, in get
context = self.get_context_data()
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/views.py", line 140, in get_context_data
Profile.objects.create(user=self.request.user)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/query.py", line 512, in create
obj = self.model(**kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/base.py", line 541, in __init__
_setattr(self, field.name, rel_obj)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 338, in __set__
super().__set__(instance, value)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 235, in __set__
raise ValueError(
ValueError: Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f682f11e220>>": "Profile.user" must be a "User" instance.
Process finished with exit code 1
How do I simulate creation of multiple users/profiles, logging in of one of them and obtaining the relevant data?
Thanks a lot in advance.
It was quite a specific question and I very much doubt anyone ever will be looking at this answer but just in case:
def test_view_get_context_data__with__logged_in_user_should_return_correct_context(self):
user_data = {'username': 'BayHuy', 'password': '11111111', }
new_user = User.objects.create_user(**user_data)
new_profile = Profile.objects.create(user=new_user)
self.assertEqual(len(User.objects.all()), 1)
self.assertEqual(new_profile.user_id, new_user.pk)
self.assertEqual(len(Profile.objects.all()), 1)
self.client.login(**user_data)
response = self.client.get(reverse('profiles-list'))
self.assertEqual(
new_profile,
response.context_data['current_profile'])
self.assertEqual(
new_profile, response.context_data['current_profile'])
self.assertEqual(len(response.context_data['profile_list']), 1)
I have created an inlineformset_factory as below :
formset = inlineformset_factory(Author, Book, form=BookForm,
formset=BaseBookFormSet,
can_order=False, can_delete=True,
extra=1, fields=('id', name)
)
BookForm is as below:
class BookForm(forms.ModelForm):
name = forms.Charfield(required=True)
def __init__(self, *args, **kwargs):
super(BookForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Div(
Field("id", type="hidden"),
Field("name"),
Field("DELETE")
)
)
class Meta:
model = Book
fields = ('id', 'name')
def clean_name(self):
book_name = self.cleaned_data['name']
try:
book = Book.objects.get(name=book_name)
return book
except:
return book_name
def clean(self):
cleaned_data = super(BookForm, self).clean()
... other operations on cleaned_data ...
def has_changed(self):
changed = super(BookForm, self).has_changed()
cleaned_data = self.clean()
... other code here ...
This is throwing an error on submitting the form :
Exception Type: AttributeError
Exception Value: 'BookForm' object has no attribute 'cleaned_data'
when formset.is_valid() is called in views.py. Traceback first shows the line in has_changed where the self.clean is being called, and then the line in clean() where the super clean is being called.
This used to work fine in django 1.10.
When I tried printing dir(self) in Django 1.10 it does show 'cleaned_data' as one of the attributes where as in Django 1.11 it does not.
Where has the 'cleaned_data' vanished in Django 1.11?
EDIT: Adding traceback:
Traceback (most recent call last):
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
response = self._get_response(request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "/vagrant/test_os/inventory/views.py", line 297, in post
if formset.is_valid():
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 321, in is_valid
self.errors
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 295, in errors
self.full_clean()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 345, in full_clean
if not form.has_changed():
File "/vagrant/test_os/inventory/forms.py", line 220, in has_changed
cleaned_data = self.clean()
File "/vagrant/test_os/inventory/forms.py", line 177, in clean
cleaned_data = super(BookForm, self).clean()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/models.py", line 344, in clean
return self.cleaned_data
AttributeError: 'BookForm' object has no attribute 'cleaned_data'
Formsets were fixed in 1.11 (in #26844) to ignore empty forms when validating the minimum number of forms. As a side-effect, formsets now call form.has_changed() on each form before validating the form. Django expects form.has_changed() to be safe to call before the form is validated, and the default implementation is indeed safe to call.
You have overridden form.has_changed() to call self.clean(), which now happens before the form is validated. Since form.clean() requires that the form is validated, this fails.
Since form.full_clean() actually calls self.has_changed(), you can't simply validate the form from within form.has_changed(). You don't show what you do in has_changed(), but it would most likely be a good idea to put this code elsewhere.
I've been trying to get the user with the highest id but no success.
This is my user model:
class User(models.Model):
email=models.EmailField(unique=True, null=False)
name=models.TextField(null=True)
Its serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'name')
The view:
class GetHighestValue(generics.ListAPIView):
serializer_class = UserSerializer
def get_queryset(self):
return User.objects.aggregate(Max('id'))
Got AttributeError when attempting to get a value for field email on
serializer UserSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the str instance.
Original exception text was: 'str' object has no attribute 'email'.
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/views.py", line 466, in dispatch
response = self.handle_exception(exc)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/views.py", line 463, in dispatch
response = handler(request, *args, **kwargs)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/generics.py", line 201, in get
return self.list(request, *args, **kwargs)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/mixins.py", line 48, in list
return Response(serializer.data)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/serializers.py", line 674, in data
ret = super(ListSerializer, self).data
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/serializers.py", line 239, in data
self._data = self.to_representation(self.instance)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/serializers.py", line 614, in to_representation
self.child.to_representation(item) for item in iterable
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/serializers.py", line 463, in to_representation
attribute = field.get_attribute(instance)
File "/home/user/.local/lib/python2.7/site-packages/rest_framework/fields.py", line 422, in get_attribute
raise type(exc)(msg)
AttributeError: Got AttributeError when attempting to get a value for field `email` on serializer `UserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `str` instance.
Original exception text was: 'str' object has no attribute 'email'.
The problem is here
def get_queryset(self):
return User.objects.aggregate(Max('id'))
The expected return value is a queryset. But aggregate does not return a queryset. Using User.objects.get() does not return a queryset either. The only way to return a queryset, is to use all() or filter()
def get_queryset(self):
return User.objects.order_by('-id')[:1]
The all() is implied here and [:1] ensures that you are returning an iterable containing an object rather than a single object.
the problem is at your view,
when you are trying to get the queryset, you are using a aggregate() method.
BUT aggregate() not return queryset, but a dictionary of name-value pairs.
see https://docs.djangoproject.com/en/1.9/topics/db/aggregation for details
Unlike aggregate(), annotate() is not a terminal clause. The output of the annotate() clause is a QuerySet; this QuerySet can be modified using any other QuerySet operation, including filter(), order_by().
Hope helpful.
I'm expanding on what zhaochy wrote. Try changing your view's get_queryset() method to the below. Instead of returning the result of the aggregate (not a queryset because it's already been evaluated), we use that number to find the instance associated with the max id and return that (which is a queryset).
class GetHighestValue(generics.ListAPIView):
serializer_class = UserSerializer
def get_queryset(self):
max_id = User.objects.aggregate(Max('id')).get('id__max')
return User.objects.filter(id=max_id)
Disclaimer: I wrote this post on my phone where I couldn't test it. Let me know in comments whether this solves your problem and I'll edit as needed.
I recently went over RESTful API in django and came across Tastypie. I came across the following questions.
If we are using hydrate or hydrate_FOO, are we still going to use obj_create? If so, under which condition that we are better-off using obj_create? Whats that used for, any simple scenario explanation?
When using hydrate, my understanding is that, we are using hydrate when we only receive POST/Update/Patch request, to transfer data in bundle.data to set correct bundle.obj.Field. Any other cases that we are going to use hydrate?
Whats the point of having full_hydrate and hydrate method if we already have hydrate_FOO? Any specific example that the first two are a MUST or better used?
Consider the following example:
data from client:
{
"A": "abc",
"B": "lifeisgood"
}
Model File:
class A(models.Model):
Astr = models.CharField(max_length=30)
class B(models.Model):
Bstr = models.CharField(max_length=30)
class C(models.Model):
a = models.ForeignKey(A)
b = models.ManyToManyField(B)
data = models.DateTime(auto_now=True)
The resource file is:
class CResource(ModelResource):
a = fields.ToOneField(AResource, "iamA")
b = fields.ToManyField(BResource, "iamB")
If I get the data from client and it is a POST, what I am currently doing is:
Inside CResource:
def hydrate_a(self, bundle):
bundle.obj.a = A.objects.get(Astr = bundle.data['A'])
return bundle
def hydrate_b(self, bundle):
bundle.obj.b = B.objects.filter(Bstr = bundle.data['B'])
return bundle
However, the result I got is:
the A resource get into the database and B resource (ManyToMany) does not.
It gives me the errors:
{
"error_message": "",
"traceback": "Traceback (most recent call last):\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 195, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 426, in dispatch_list\n return self.dispatch('list', request, **kwargs)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 458, in dispatch\n response = method(request, **kwargs)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 1320, in post_list\n updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 2084, in obj_create\n return self.save(bundle)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 2235, in save\n self.save_m2m(m2m_bundle)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django_tastypie-0.10.0-py2.6.egg/tastypie/resources.py\", line 2326, in save_m2m\n related_mngr = getattr(bundle.obj, field_object.attribute)\n\n File \"/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/fields/related.py\", line 279, in get\n raise self.related.model.DoesNotExist\n\nDoesNotExist\n"
}
Any idea why this is not correct? What would be the suggested way to save a foreignkey or manytomany related data when do a POST, from the data given in front end.
Many thanks!
-leo
I'd like to extend django's auth.User, after reading this question,
this is my models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
age = models.SmallIntegerField()
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
and I add this line in the bottom of my settings.py:
AUTH_PROFILE_MODULE = 'MYAPP.UserProfile'
The problem is, when I run python manage.py shell, and type:
from django.contrib.auth.models import User
user = User()
It works without problem! why doesn't it give an error that I didn't give a username/password?
When you run user = User(), all you are doing is creating a new User instance. It will not throw an error until you try to save it with user.save().
To simultaneously create and save a new model instance to the DB:
user = User.objects.create() # should throw an IntegrityError due to required (not NULL) fields not provided
Please ensure that UserProfile is inside of models.py in MYAPP and MYAPP is registered in INSTALLED_APPS. It seems like your signal doesn't work at all. If it won't help, try that in shell:
from django.contrib.auth.models import User
from MYAPP.models import UserProfile
user = User()
Thus you'll be sure that signal was properly registered.
The restrictions are mainly designed for form validation; there are very few restrictions (mainly those that may cause DB problems) that are enforced when you use the API.
You can easily create an empty user (a user with no username):
>>> from django.contrib.auth.models import User
>>> u = User()
>>> u.save()
>>> u
<User: >
>>> u.id
2
However, if you try to create two empty users you'll get an IntegrityError:
>>> u = User()
>>> u.save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/base.py", line 460, in save
self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/base.py", line 553, in save_base
result = manager._insert(values, return_id=update_pk, using=using)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/manager.py", line 195, in _insert
return insert_query(self.model, values, **kwargs)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/query.py", line 1436, in insert_query
return query.get_compiler(using=using).execute_sql(return_id)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 791, in execute_sql
cursor = super(SQLInsertCompiler, self).execute_sql(None)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 735, in execute_sql
cursor.execute(sql, params)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/backends/util.py", line 34, in execute
return self.cursor.execute(sql, params)
File "/Users/burhan/work/projects/foo/env/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 234, in execute
return Database.Cursor.execute(self, query, params)
IntegrityError: column username is not unique
If you look at the model, you'll see there is a db-level restriction on the username field (unique=True):
`username = models.CharField(_('username'), max_length=30, unique=True...`
This is enforced at the API level, so you cannot have two users with the same username field.
Another example of this is the choices argument. This is used chiefly for presentation. If you have a field with choices=('M','Male'),('F','Female'); using the API you can insert any single character and it will happily accept it.
Options that are enforced at the database level (meaning, you can't "violate" them from the API) are:
unique
max_length
null (do not confuse with blank)