views.py:
class ajax_profile():
def __init__(self, request):
username = request.REQUEST.get('username','')
email = request.REQUEST.get('email','')
password = request.REQUEST.get('password','')
action = request.REQUEST.get('action','')
options = {
'check_username' : self.check_username(username),
'check_email' : self.check_email(email),
'forgot_password' : self.forgot_password(email),
'login' : self.login(username, password),
}
options[action]
def check_username(self, username):
return HttpResponse('Test %s' % username)
def check_email(self, email):
pass
def forgot_password(self, email):
pass
def login(self, username, password):
pass
urls.py
(r'^ajax_profile/$', 'apps.profiles.views.ajax_profile'),
URL to call
ajax_profile/?action=check_username&username=testtest
ERROR: instance has no attribute 'status_code'
Why?
I don't recommend doing things this way. Your views should return HttpResponse objects, while ajax_profile's init method should initialize an instance of ajax_profile.
If you must, you can try having ajax_profile subclass HttpResponse, and use super to initialize the HttpResponse at the end of ajax_profile's __init__:
class ajax_profile(HttpResponse):
def __init__(self, request):
# ...
response_text = options[action]()
super(ajax_profile, self).__init__(response_text)
Also worth noting, the way options is set up, every method in the dictionary (check_username, check_email, etc) will be run every time regardless of the action. Probably not what you want.
your last line in init() should be return options[action]
Related
I'm having issues serializing a 3rd party package (django-organizations) as I want to receive the context in JSON.
The class itself looks like this:
class OrganizationUserMixin(OrganizationMixin, JSONResponseMixin):
"""Mixin used like a SingleObjectMixin to fetch an organization user"""
user_model = OrganizationUser
org_user_context_name = 'organization_user'
def get_user_model(self):
return self.user_model
def get_context_data(self, **kwargs):
kwargs = super(OrganizationUserMixin, self).get_context_data(**kwargs)
kwargs.update({self.org_user_context_name: self.object,
self.org_context_name: self.object.organization})
return kwargs
def get_object(self):
""" Returns the OrganizationUser object based on the primary keys for both
the organization and the organization user.
"""
if hasattr(self, 'organization_user'):
return self.organization_user
organization_pk = self.kwargs.get('organization_pk', None)
user_pk = self.kwargs.get('user_pk', None)
self.organization_user = get_object_or_404(
self.get_user_model().objects.select_related(),
user__pk=user_pk, organization__pk=organization_pk)
return self.organization_user
And I'm trying to pass this custom JSONResponseMixin to my OrganizationUserMixin class:
class JSONResponseMixin:
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return JsonResponse(
self.get_data(context),
**response_kwargs
)
def get_data(self, context):
print(context)
return context
And then overriding the render_to_response in OrganizationUserMixin as such:
def render_to_response(self, context, **response_kwargs):
return self.render_to_json_response(context, **response_kwargs)
If I print context
It looks something like this
# {
# 'object': <OrganizationUser: Erik (MyOrgName)>,
# 'organizationuser': <OrganizationUser: Erik (MyOrgName)>,
# 'organization': <Organization: MyOrgName>,
# 'view': <organizations.views.OrganizationUserDetail object at 0x1091a3ac0>,
# 'organization_user': <OrganizationUser: Erik (MyOrgName)>
# }
The error message I get is TypeError: Object of type OrganizationUser is not JSON serializable
How can I serialize the context in my JSONResponseMixin?
You have two options here, either use Django rest framework (DRF) or implement functions that performs serialization for the models.
Option 1
DRF is a more sustainable solution as you grow the API side of your application, as it would abstract most of de/serialization work, and provide you with alot of other useful functionalities, such as Routers, ViewSets, and other.
Example Code
# serializers.py
from rest_framework import serializers
class OrganizationUserSerializer(serializers.ModelSerializer):
class Meta:
model = OrganizationUser
fields = '__all__'
# views.py
from rest_framework import generics
class OrganizationUser(generics.RetrieveModelMixin):
queryset = OrganizationUser.objects.all()
serializer_class = OrganizationUserSerializer
Option 2
That being said, if you the JsonResponseMixin is sufficient for most of your needs, and your application is not mainly reliant on the API, you can get away with just adding serialization functions for your models and calling them in your JsonResponseMixin.get_data()
Example code:
# Models.py
class OrganizationUser(models.Model):
...
def to_json(self):
# assuming you have a field name and organization
return {"name": self.name, "organization": self.organization.to_json()}
# mixins.py
class JSONResponseMixin:
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return JsonResponse(
self.get_data(context),
**response_kwargs
)
def get_data(self, context):
data = {}
for key, val in context:
if hasattr(val, "to_json"):
data[key] = val.to_json()
else:
data[key] = val
return data
I stumbled upon a code that is used to provide some args to the request method. Problem is that I'm not that sure if it is the cleanest way to handle this case.
def check_permissions(check_mixins):
"""
:param check_mixins: is given to the inner decorator
Decorator that will automatically populate some parameters when
using dispatch() toward the right method (get(), post())
"""
def _decorator(_dispatch):
def wrapper(request, *args, **kwargs):
Is it a problem if "self" isn't passed in the method definition in here...
for mixin in check_mixins:
kwargs = mixin.check(request, *args, **kwargs)
if isinstance(kwargs, HttpResponseRedirect):
return kwargs
return _dispatch(request, *args, **kwargs)
return wrapper
return _decorator
class UserLoginMixin(object):
def check(request, *args, **kwargs):
... and here ? It seems so ugly in my IDE
user = request.user
if user.is_authenticated() and not user.is_anonymous():
kwargs['user'] = user
return kwargs
return redirect('user_login')
class AppoExistMixin(object):
def check(request, *args, **kwargs):
Here too...
appo_id = kwargs['appo_id']
try:
appoff = IdAppoff.objects.get(id=appo_id)
kwargs['appoff'] = appoff
del kwargs['appo_id']
return kwargs
except IdAppoff.DoesNotExist:
pass
messages.add_message(request, messages.ERROR,
"Item doesn't exist!")
return redirect('home')
class SecurityMixin(View):
"""
Mixin that dispatch() to the right method with augmented kwargs.
kwargs are added if they match to specific treatment.
"""
data = []
def __init__(self, authenticators):
super(SecurityMixin, self).__init__()
# Clearing data in order to not add useless param to kwargs
self.data.clear()
# Build the list that contain each authenticator providing
# context increase
for auth in authenticators:
self.data.append(auth)
#method_decorator(check_permissions(data))
Why data and not self.data ? How is it possible ?
def dispatch(self, request, *args, **kwargs):
return super(SecurityMixin, self).dispatch(request, *args, **kwargs)
Each view then inherits from SecurityMixin and got authenticators = [UserLoginMixin, ...] as class attribute.
The problem I have sometimes (I can't reproduce the bug...) is that I got KeyError on augmented kwargs while URL definition is properly set. eg:
appo_id = kwargs['appo_id']
KeyError: 'appo_id'Exception
I've been looking for hours and it seems that I will never have the solution... It's a bit frustrating.
If someone could help It'll be greatly appreciated.
I have a hunch that improper handling of class attributes is at fault.
CLASS VS INSTANCE
The class attribute data is overwritten every time SecurityMixin.__init__ is called:
class A:
data = []
def __init__(self, *args):
self.data.clear() # self.data references the class attribute
for x in args:
self.data.append(x)
x = A('foo')
# A.data = ['foo']
# x.data = ['foo']
y = A('bar')
# A.data = ['bar']
# y.data = ['bar']
# x.data = ['bar'] !!
HOWEVER:
class A:
data = ['I am empty']
def __init__(self, *args):
self.data = [] # redeclaring data as an instance attribute
for x in args:
self.data.append(x)
x = A('foo')
# A.data = ['I am empty']
# x.data = ['foo']
y = A('bar')
# A.data = ['I am empty']
# y.data = ['bar']
# x.data = ['foo']
This class attribute data is passed to the decorator (you cannot pass an instance attribute to a method decorator, i.e. self.data, because the instance does not yet exist during decorator declaration).
The wrapped function, however, does have access to the instance if it is passed in ('self' argument).
Django's method_decorator removes this self argument; that decorator is used to transform a function decorator (which does not get a self argument implicitly) into a method decorator (which gets a self parameter implicitly). That's why you do not have to include self in the list of parameters for the various mixin check methods as it was removed by method_decorator. To put it simply: use method_decorator to decorate a method with a function decorator. Read up on it here decorating CBVs.
Knowing that, I am not really sure why check_permissions should be a function decorator as it is now when you only use it to decorate methods.
You could just decorate dispatch with check_permissions itself:
def check_permissions(_dispatch):
def _decorator(self, request, *args, **kwargs): # adding self
for mixin in self.data: # referencing the INSTANCE data
kwargs = mixin.check(request, *args, **kwargs)
if isinstance(kwargs, HttpResponseRedirect):
return kwargs
return _dispatch(self, request, *args, **kwargs) # don't forget self here
return _decorator
#check_permissions
def dispatch(self, request, *args, **kwargs):
...
Maybe some view is trying to check AppoExistMixin because it is in that view's data list, although it should not be - and the view's kwargs do not include 'appo_id'. You could also try being explicit by passing the wanted check mixins directly to the decorator: #method_decorator(check_permissions([UserLoginMixin, ...])). This way you you don't have to mess with class vs instance attributes.
Also... you should rename data to something that you are unlikely to overwrite with your own variable.
If you want to be super-lazy you could just do:
appo_id = kwargs.get('appo_id',False)
if not appo_id: return kwargs
But this would only fix that particular error in that one view. It's ignoring a symptom instead of curing the disease.
Some more explanation:
function vs method. check_permissions is a function, while dispatch() is a method. You cannot simply use a function decorator on a method: for one, because the implicit argument self (the instance the method belongs to) is passed to the decorator as well, although it may not expect it.
That is where django's method_decorator comes in by removing and storing self within the decorator. Compare the two signatures: wrapper(request, *args, **kwargs) vs _decorator(self, request, *args, **kwargs). In the former, method_decorator 'absorbed' self before the function decorator is called.
Think of it as an adapter, a decorator for the decorator, that 'bridges the gap' between function and method. Use it if you don't want to/cannot alter the decorator.
In your case, however, you can change the decorator to make it work with a method - thus you don't need django's method_decorator.
Using Django, I'm looking for a way to use one url patern (with slug) to query one model and if nothing is found query a second model. I'm using Class Based Views.
I am following this answer, and the next View is being called. But then I get the following error:
"Generic detail view must be called with either an object pk or a slug."
I can't figure out how to pass the slug to the next View.
My url:
url(r'^(?P<slug>[-\w]+)/$', SingleView.as_view(), name='singleview'),
My CBV's:
class SingleView(DetailView):
def dispatch(self, request, *args, **kwargs):
post_or_page_slug = kwargs.pop('slug')
if Page.objects.filter(slug=post_or_page_slug).count() != 0:
return PageDetailView.as_view()(request, *args, **kwargs)
elif Post.objects.filter(slug=post_or_page_slug).count() != 0:
return PostDetailView.as_view()(request, *args, **kwargs)
else:
raise Http404
class PageDetailView(DetailView):
model = Page
template_name = 'page-detail.html'
class PostDetailView(DetailView):
model = Post
template_name = 'post-detail.html'
The problem is that you are popping the slug, which removes it from kwargs. This means that the slug is not getting passed to the view.
You can change it to:
post_or_page_slug = kwargs.pop['slug']
I would usually discourage calling MyView.as_view(request, *args, **kwargs) inside another view. Class based views are intended to be extended by subclassing, not by calling them inside other views.
For the two views in your example, you could combine them into a single view by overriding get_object and get_template_names.
from django.http import Http404
class PageOrPostDetailView(DetailView):
def get_object(self):
for Model in [Page, Post]:
try:
object = Model.objects.get(slug=self.kwargs['slug'])
return object
except Model.DoesNotExist:
pass
raise Http404
def get_template_names(self):
if isinstance(self.object, Page):
return ['page-detail.html']
else:
return ['post-detail.html']
I want to set an age restriction on the account Sign up Process implemented by django-user-accounts. I have added a field to the SignupForm like the example in the docs. In my customized view I have the following:
import user_accounts_custom.forms
from profiles.models import ArtistProfile, UserProfile
from datetime import date
import math
class SignupView(SignupView):
form_class = user_accounts_custom.forms.SignupForm
def create_user(self, form, commit=True, **kwargs):
old_enough = self.birthday_check(form)
if old_enough:
return super(SignupView, self).create_user(self, form,
commit=True, **kwargs)
else:
return super(SignupView, self).create_user(self, form,
commit=False, **kwargs)
def birthday_check(self, form):
birthdate = form.cleaned_data["birthdate"]
fraud_detect = abs(date.today() - birthdate)
if ( (fraud_detect.days / 365.0) < 13 ):
# WHAT ABOUT THE BABIES!!!!
return False
else:
return True
Setting commit to False is giving me a type error further in the create_user method on the SignupView instance because it attempts to return a user object but, like I wanted, it didn't create one. I want to send an HttpResponseForbidden object or a message but I'm not sure how to implement it here given the context. The other option I am considering is using a dummy user object (specifically my Anonymous User object) and simply redirecting without creating an account; I'm not sure which path is simplest.
This answer helped me solve the problem, here is how I implemented it:
def clean(self):
cleaned_data = super(SignupForm, self).clean()
bday = self.cleaned_data["birthdate"]
fraud_detect = abs(date.today() - bday)
if ( (fraud_detect.days / 365.0) < 13 ):
# WHAT ABOUT THE BABIES!!!!
raise forms.ValidationError("Sorry, you cannot create an account.",
code="too_young",
)
return cleaned_data
The trick was to intercept the clean() method in the forms.py I created to customize django-user-accounts.
Some additional links to help with validation (NOTE: these links go to django version 1.6):
Form and Field
Validation
Validators
I am converting an old project with Class Views and I want to know if the pattern I am using is "safe".
In brief I have a class View like this (code simplified ):
class FileAddHashedView(FormView):
"""This view hashes the file contents using md5"""
form_class = FileUploadForm
success_url = reverse_lazy('vault-show')
template_name = "vault/add.html"
filebox_random= 0
def get_success_url(self):
return reverse('vault-show', kwargs={'random': self.filebox_random})
def form_valid(self, form):
instance = form.save(commit=False)
#generate a random number
rng = random.SystemRandom()
#TODO: must catch exception here
instance.random=rng.randint(0, sys.maxint)
instance.save()
#TODO: check what is the proper way to generate the parametric URL
self.filebox_random=instance.random
messages.success(
self.request, 'File hashed and uploaded!', fail_silently=True)
return super(FileAddHashedView, self).form_valid(form)
def form_invalid(self, form, **kwargs):
messages.error(self.request, 'Upload failed ...', fail_silently=True)
return super(FileAddView, self).form_invalid(form)
And the corresponding views that gets activated:
class VaultStatus(TemplateView):
template_name = 'vault/vault.html'
def get(self, request,random):
# retrieve object info
data = {
'filebox': FileBox.objects.get(random=random)
}
return render(request,self.template_name,data)
#return render_to_response('vault/vault.html', {'random': random});
Now my question is: is there a better way in get_success_url to get the self.filebox_random without relying on a class attribute?
Is this thread-safe?
You're not using a class attribute here. Even though you've defined filebox_random at class level, by setting a value to that name within an instance method in Python you're actually creating an instance attribute with the same name, which hides the class-level one. (Although I'm not actually sure why you're defining the class attribute in the first place).
Django does quite a lot of work behind the scenes to ensure that class-based views are thread-safe, so there is no danger in setting instance attributes.