Understandings toward Django Tastypie - python

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

Related

Test of Django ProfileListView fails with ValueError: Cannot assign "<SimpleLazyObject:....>": "Profile.user" must be a "User" instance

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)

binascii.Error: Incorrect padding in python django

I am trying to save the base64 encoded image in the django rest framework. First of all, we make a code to insert the base64 encoded image into the imagefield and test it, and the following error appears.
binascii.Error: Incorrect padding
What I don't understand is that I've used the same code before and there was no such error. Can you help me? Here is my code.
serializers.py
from rest_framework import serializers
from .models import post, comment
class Base64ImageField (serializers.ImageField) :
def to_internal_value (self, data) :
from django.core.files.base import ContentFile
import base64
import six
import uuid
if isinstance(data, six.string_types):
if 'data:' in data and ';base64,' in data :
header, data = data.split(';base64,')
try :
decoded_file = base64.b64decode(data)
except TypeError :
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension, )
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension (self, file_name, decoded_file) :
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
class commentSerializer (serializers.ModelSerializer) :
class Meta :
model = comment
fields = '__all__'
class postSerializer (serializers.ModelSerializer) :
author = serializers.CharField(source='author.username', read_only=True)
image1 = Base64ImageField(use_url=True)
image2 = Base64ImageField(use_url=True)
image3 = Base64ImageField(use_url=True)
image4 = Base64ImageField(use_url=True)
image5 = Base64ImageField(use_url=True)
comment = commentSerializer(many=True, read_only=True)
class Meta:
model = post
fields = ['pk', 'author', 'title', 'text', 'image1', 'image2', 'image3', 'image4', 'image5', 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'comment']
I'm not sure this applies to your situation, depending on where you're storing your encoded data.
I had the same error, but it related to some encoded session data. I cleared out the session data (cookies, cache etc) in the browser Devtools, and it fixed my issue.
Just posting this in case it applies or helps others who come along for the same reason.
Run following command in shell
from django.contrib.sessions.models import Session
Session.objects.all().delete()
More information at https://code.djangoproject.com/ticket/31592
I had the same error. I do all things clear cache but it doesn't work. Now change the browser to Mozilla. Now it's working.
I had the same issue. I guess it was caused by me using django 4.0.1 in the beginning and later switching back to django 2.2... (Maybe your issue was not caused by this but I just want to provide some idea on where the problem might be to all the reader who has this issue and visit this page.)
Django 4.0.1 should directly save the session data in string into the database but Django 2.2 saves and reads base64 encoded data into / from the same column session_data in table django_session in DB.
The string failed in base64.b64decode() in my case is .eJxVjEEOwiAQRe_C2pABCgWX7j0DmRlAqoYmpV0Z765NutDtf-_9l4i4rTVuPS9xSuIslDj9boT8yG0H6Y7tNkue27pMJHdFHrTL65zy83K4fwcVe_3WGtkEHfLg2IMroL1VZA0BFGMJPJhRkdEucypY2CfA7C2HgRHGor14fwDNWjfC:1nERxl:5jJRHXpQH7aZrf2-C99MnTIWARd_cUag76Xa2YjW1yw, which is obviously not valid base64 string since symbols : - . . do not exist in base64 character list at all.
My full traceback info is:
Internal Server Error: /admin
Traceback (most recent call last):
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 189, in _get_session
return self._session_cache
AttributeError: 'SessionStore' object has no attribute '_session_cache'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 241, in wrapper
return self.admin_view(view, cacheable)(*args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\views\decorators\cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 212, in inner
if not self.has_permission(request):
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 186, in has_permission
return request.user.is_active and request.user.is_staff
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\functional.py", line 256, in inner
self._setup()
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\functional.py", line 392, in _setup
self._wrapped = self._setupfunc()
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\middleware.py", line 24, in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\middleware.py", line 12, in get_user
request._cached_user = auth.get_user(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\__init__.py", line 182, in get_user
user_id = _get_user_session_key(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\__init__.py", line 59, in _get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 54, in __getitem__
return self._session[key]
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 194, in _get_session
self._session_cache = self.load()
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\db.py", line 44, in load
return self.decode(s.session_data) if s else {}
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 100, in decode
encoded_data = base64.b64decode(session_data.encode('ascii'))
It's possible you run the server in a different Environment, Activate that environment and try to run the server again.
incase the env folder is in your current Path here's what to do
kill the server and then run
source env/bin/activate
python manage.py runserver

Random NoTransaction in Pyramid

I'm having trouble identifying the source of transaction.interfaces.NoTransaction errors within my Pyramid App. I don't see any patterns to when the error happens, so to me it's quite random.
This app is a (semi-) RESTful API and uses SQLAlchemy and MySQL. I'm currently running within a docker container that connects to an external (bare metal) MySQL instance on the same host OS.
Here's the stack trace for a login attempt within the App. This error happened right after another login attempt that was actually successful.
2020-06-15 03:57:18,982 DEBUG [txn.140501728405248:108][waitress-1] new transaction
2020-06-15 03:57:18,984 INFO [sqlalchemy.engine.base.Engine:730][waitress-1] BEGIN (implicit)
2020-06-15 03:57:18,984 DEBUG [txn.140501728405248:576][waitress-1] abort
2020-06-15 03:57:18,985 ERROR [waitress:357][waitress-1] Exception while serving /auth
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/waitress/channel.py", line 350, in service
task.service()
File "/usr/local/lib/python3.8/site-packages/waitress/task.py", line 171, in service
self.execute()
File "/usr/local/lib/python3.8/site-packages/waitress/task.py", line 441, in execute
app_iter = self.channel.server.application(environ, start_response)
File "/usr/local/lib/python3.8/site-packages/pyramid/router.py", line 270, in __call__
response = self.execution_policy(environ, self)
File "/usr/local/lib/python3.8/site-packages/pyramid_retry/__init__.py", line 127, in retry_policy
response = router.invoke_request(request)
File "/usr/local/lib/python3.8/site-packages/pyramid/router.py", line 249, in invoke_request
response = handle_request(request)
File "/usr/local/lib/python3.8/site-packages/pyramid_tm/__init__.py", line 178, in tm_tween
reraise(*exc_info)
File "/usr/local/lib/python3.8/site-packages/pyramid_tm/compat.py", line 36, in reraise
raise value
File "/usr/local/lib/python3.8/site-packages/pyramid_tm/__init__.py", line 135, in tm_tween
userid = request.authenticated_userid
File "/usr/local/lib/python3.8/site-packages/pyramid/security.py", line 381, in authenticated_userid
return policy.authenticated_userid(self)
File "/opt/REDACTED-api/REDACTED_api/auth/policy.py", line 208, in authenticated_userid
result = self._authenticate(request)
File "/opt/REDACTED-api/REDACTED_api/auth/policy.py", line 199, in _authenticate
session = self._get_session_from_token(token)
File "/opt/REDACTED-api/REDACTED_api/auth/policy.py", line 320, in _get_session_from_token
session = service.get(session_id)
File "/opt/REDACTED-api/REDACTED_api/service/__init__.py", line 122, in get
entity = self.queryset.filter(self.Meta.model.id == entity_id).first()
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3375, in first
ret = list(self[0:1])
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3149, in __getitem__
return list(res)
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3481, in __iter__
return self._execute_and_instances(context)
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3502, in _execute_and_instances
conn = self._get_bind_args(
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3517, in _get_bind_args
return fn(
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3496, in _connection_from_session
conn = self.session.connection(**kw)
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1138, in connection
return self._connection_for_bind(
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1146, in _connection_for_bind
return self.transaction._connection_for_bind(
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 458, in _connection_for_bind
self.session.dispatch.after_begin(self.session, self, conn)
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/event/attr.py", line 322, in __call__
fn(*args, **kw)
File "/usr/local/lib/python3.8/site-packages/zope/sqlalchemy/datamanager.py", line 268, in after_begin
join_transaction(
File "/usr/local/lib/python3.8/site-packages/zope/sqlalchemy/datamanager.py", line 233, in join_transaction
DataManager(
File "/usr/local/lib/python3.8/site-packages/zope/sqlalchemy/datamanager.py", line 89, in __init__
transaction_manager.get().join(self)
File "/usr/local/lib/python3.8/site-packages/transaction/_manager.py", line 91, in get
raise NoTransaction()
transaction.interfaces.NoTransaction
The trace shows that the execution eventually reaches my project, but only my custom authentication policy. And it fails right where the database should be queried for the user.
What intrigues me here is the third line on the stack trace. It seems Waitress somehow aborted the transaction it created? Any clue why?
EDIT: Here's the code where that happens: policy.py:320
def _get_session_from_token(self, token) -> UserSession:
try:
session_id, session_secret = self.parse_token(token)
except InvalidToken as e:
raise SessionNotFound(e)
service = AuthService(self.dbsession, None)
try:
session = service.get(session_id) # <---- Service Class called here
except NoResultsFound:
raise SessionNotFound("Invalid session found Request headers. "
"Session id: %s".format(session_id))
if not service.check_session(session, session_secret):
raise SessionNotFound("Session signature does not match")
now = datetime.now(tz=pytz.UTC)
if session.validity < now:
raise SessionNotFound(
"Current session ID {session_id} is expired".format(
session_id=session.id
)
)
return session
And here is an a view on the that service class method:
class AuthService(ModelService):
class Meta:
model = UserSession
queryset = Query(UserSession)
search_fields = []
order_fields = [UserSession.created_at.desc()]
# These below are from the generic ModelClass father class
def __init__(self, dbsession: Session, user_id: str):
self.user_id = user_id
self.dbsession = dbsession
self.Meta.queryset = self.Meta.queryset.with_session(dbsession)
self.logger = logging.getLogger("REDACTED")
#property
def queryset(self):
return self.Meta.queryset
def get(self, entity_id) -> Base:
entity = self.queryset.filter(self.Meta.model.id == entity_id).first()
if not entity:
raise NoResultsFound(f"Could not find requested ID {entity_id}")
As you can see, the there's already some exception treatment. I really don't see what other exception I could try to catch on AuthService.get
I found the solution to be much simpler than tinkering inside Pyramid or SQLAlchemy.
Debugging my Authentication Policy closely, I found out that my it was keeping a sticky reference for the dbsession. It was stored on the first request ever who used it, and never released.
The first request works as expected, the following one fails: My understanding is that the object is still in memory while the app is running, and after the initial transaction is closed. The second request has a new connection, and a new transaction, but the object in memory still points to the previous one, that when used ultimately causes this.
What I don't understand is why the exception didn't happen sometimes. As I mentioned initially, it was seemingly random.
Another thing that I struggled with was in writing a test case to expose the issue. On my tests, the issue never happens because I have (and I've never seen it done differently) a single connection and a single transaction throughout the entire testing session, as opposed of a new connection/transaction per request, so I have not found no way to actually reproduce.
Please let me know if that makes sense, and if you can shed a light on how to expose the bug on a test case.

Tastypie relationship fields for more than 2 models

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')

Error when using Django Rest Framework's APITestCase

I'm trying to run some tests for my Django-Rest-Framework API but am stuck on an error. When I run the following tests, I get the following errors.
Traceback (most recent call last):
File "C:\Users\Bill\SD\DjangoApps\vidapp\startapp\tests.py", line 21, in test_get_user
response = self.client.get('/user/1/')
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 473, in get
response = super(Client, self).get(path, data=data, **extra)
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 280, in get
return self.request(**r)
File "C:\Anaconda\lib\site-packages\rest_framework\test.py", line 143, in request
return super(APIClient, self).request(**kwargs)
File "C:\Anaconda\lib\site-packages\rest_framework\test.py", line 95, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "C:\Anaconda\lib\site-packages\django\test\client.py", line 444, in request
six.reraise(*exc_info)
File "C:\Anaconda\lib\site-packages\django\core\handlers\base.py", line 114, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
TypeError: __init__() takes exactly 1 argument (2 given)
Test Cases:
class UserTestCase(APITestCase):
def setUp(self):
helper.reset_test_db()
def test_get_user(self):
response = self.client.get('/user/1/')
print response.content
self.assertEqual(response.data, {'fname':'Generic','lname':'Name','token':'token1'})
URL Config:
url(r'^user/new/$', 'startapp.views.new_user'),
url(r'^user/1/$', 'startapp.views.get_1'),
Views:
class get_1(APIView):
def get(self, request):
user = db_models.User.objects.get(pk=1)
if(user is not None):
serial_user = serial.UserSerializer(user)
return Response(serial_user.data)
else:
return Response(status.HTTP_404_NOT_FOUND)
I know the view itself works because I tested that separately. The data is definitely present since helper.reset_test_db() puts it there (I know I should be using fixtures but this is for testing so I went with the simple route). The same error occurs for POST and other commands or when I use Django's TestCase instead of APITestCase. While this is my first time using Django's TestCase, I read both the Django and Django rest documents but can't seem to figure out this issue.
The view in your case is a class-based view.
So you have to add it to the urlconfig with as_view:
url(r'^user/1/$', startapp.views.get_1.as_view()),

Categories