Override a specific argument in all django model queries? - django

I have a custom django user model that does not contain a username field, it uses email instead. I am attempting to implement a 3rd party package that makes queries to the user model based on username. Is there a way to override all queries (get, filter, etc.) to this model through a custom manager or otherwise so that username is simply converted to email?
It would turn this:
User.objects.filter(username="grrrrrr#grrrrrr.com")
into
User.objects.filter(email="grrrrrr#grrrrrr.com")

You can create a QuerySet subclass where you "clean" the incoming kwargs for the get and filter methods and change username to email
class UserQuerySet(models.QuerySet):
def _clean_kwargs(self, kwargs):
if 'username' in kwargs:
kwargs['email'] = kwargs['username']
del kwargs['username']
return kwargs
def filter(self, *args, **kwargs):
kwargs = self._clean_kwargs(kwargs)
return super().filter(*args, **kwargs)
def get(self, *args, **kwargs):
kwargs = self._clean_kwargs(kwargs)
return super().get(*args, **kwargs)
class UserManager(BaseUserManager):
def get_queryset(self):
return UserQuerySet(self.model, using=self._db)
class User(AbstractBaseUser):
objects = UserManager()
The custom manager will need to implement create_user and create_superuser as detailed here in the docs

Related

Encrypt Django auth User model's fields

I am trying to encrypt my username and email field in Djago's Default Auth model using a Proxy Model method but so far with no luck. Here is what I have attempted so far:
class UserManager(models.Manager):
def from_db_value(self, value, expression, connection):
if value is None or value == '':
return value
# decrypt db value and return
return decrypt(value)
def get_prep_value(self, value):
# prepare value to be saved to the database.
return encrypt(str(value))
class CustomUser(User):
objects = UserManager()
class Meta:
proxy = True
def print_value(self, value):
print('test', value)
def save(self, *args, **kwargs):
self.user = self.print_value(self.user)
super().save(*args, **kwargs)
I am trying to overwrite the User model with Proxy model and a custom Model manager. Any tips whether how can I achieve this would be really appreciated.

Django: Current User Id for ModelForm Admin

I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form

Dynamically include or exclude Serializer class fields

In my User profile model I've included a show_email field explicitly. So, to add this feature to my API, the UserSerializer class looks like this:
class UserSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField('show_email')
def show_email(self, user):
return user.email if user.show_email else None
class Meta:
model = django.contrib.auth.get_user_model()
fields = ("username", "first_name", "last_name", "email")
But I don't really like it. I think it would be a lot cleaner if the field email would be completely excluded from the serializer output it show_email is False, instead showing that ugly "email": null thing.
How could I do that?
You could do this in your API view by overriding the method returning the response, i.e. the "verb" of the API view. For example, in a ListAPIView you would override get():
class UserList(generics.ListAPIView):
model = django.contrib.auth.get_user_model()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
response = super(UserList, self).get(request, *args, **kwargs)
for result in response.data['results']:
if result['email'] is None:
result.pop('email')
return response
You would probably want to add some more checking for attributes, but that's the gist of how it could be done. Also, I would add that removing fields from some results may cause issues for the consuming application if it expects them to be present for all records.
This answer comes late but for future google searches: there is an example in the documentation about Dynamically modifying fields.
So, by passing an argument to the serializer, you control whether or not a field is processed:
serializer = MyPostSerializer(posts, show_email=permissions)
and then in the init function in the serializer you can do something like:
class MyPostSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
show_email = kwargs.pop('show_email', None)
# Instantiate the superclass normally
super(DeviceSerializer, self).__init__(*args, **kwargs)
if not show_email:
self.fields.pop("show_email")
Now the show_email field will be ignored by the serializer.
You could override restore_fields method on serializer. Here in restore_fields method you can modify list of fields - serializer.fields - pop, push or modify any of the fields.
eg: Field workspace is read_only when action is not 'create'
class MyPostSerializer(ModelSerializer):
def restore_fields(self, data, files):
if (self.context.get('view').action != 'create'):
self.fields.get('workspace').read_only=True
return super(MyPostSerializer, self).restore_fields(data, files)
class Meta:
model = MyPost
fields = ('id', 'name', 'workspace')
This might be of help...
To dynamically include or exclude a field in an API request, the modification of
#stackedUser's response below should do:
class AirtimePurchaseSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
try:
phone = kwargs['data']['res_phone_number']
except KeyError:
phone = None
# Instantiate the superclass normally
super(AirtimePurchaseSerializer, self).__init__(*args, **kwargs)
if not phone:
self.fields.pop("res_phone_number")
res_phone_number = serializers.CharField(max_length=16, allow_null=False)

Using the Django Rest Framework

I have the following model that basically stores a random hash value for each tag associated with a particular user.
class PublicTags(models.Model):
tag = models.ForeignKey(Tag, related_name='hashes')
hash_value = models.CharField(max_length=103)
user = models.ForeignKey(User, related_name='tags')
public = models.BooleanField(default=False)
class Meta:
unique_together = ('tag', 'user')
verbose_name_plural = 'Public Tag Hashes'
def __unicode__(self):
return u'%s: %s' % (self.tag, self.user, self.hash_value)
I am trying to use the Django Rest Framework to do the following:
Create a view that will be accessed at api/v1/public_tags.
I will use AJAX to post data using the Django Rest API.
On a post, the system does the following:
It checks to see if there is already a Public Tag row for the tag id (sent via post)
If not, it creates a random hash_value for that tag and user.
I am confused about how to use the Django Rest Framework to accomplish this task.
I got this far:
class PublicTagView(RetrieveUpdateAPIView):
model = PublicTags
serializer_class = serializers.PublicTagSerializer
permission_classes = (IsAuthenticated,)
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.SingleObjectAPIView):
"""
Concrete view for retrieving or updating a model instance.
FIXME: the newest version of rest_framework has this class
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
As far as I understand you need to query an individual record and create a record.
In this case generic class based views are fine enough to start with.
You can then map these views in your urls.py like this:
urlpatterns = patterns('',
url(r'^api/v1/public_tags/$', views.PublicTagsList.as_view()),
url(r'^api/v1/public_tags/(?P<pk>[0-9]+)/$', views.PublicTagsDetail.as_view()),
)
This is valid if you use primary key to fetch individual record.

ModelForm User Mixin

I've got some models with user field.
For this purpose I'd like to create a form mixin that would add self.user instance (which is provided to the form in views). Is it possible ?
Here's the example
class UserFormMixin(object):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, **kwargs):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if kwargs['commit']:
return obj.save()
else:
return obj
What I'd like to achieve:
class SomeFormWithUserField(UserFormMixin, ModelForm):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
if kwargs['commit']:
return data.save()
else
return data
class SomeOtherFormWithUser(UserFormMixin, ModelForm):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# no need to save here.. standard model form with user prepended on save()
The problem is that UserFormMixin doesn't know about model instance? Or am I wrong here?
I am getting some problems.. like 'commit' kwargs key error.. or object is not saved..
You're close, you just have some logic errors. First, in order to override ModelForm methods, your mixin needs to inherit from ModelForm.
class UserFormMixin(forms.ModelForm):
...
Then, any forms that inherit from it just inherit UserFormMixin, not ModelForm.
class SomeOtherFormWithUser(UserFormMixin):
...
Second, your __init__ method override is incorrect. You need to accept any and all args and kwargs that get passed into it.
def __init__(self, *args, **kwargs):
...
Finally, don't override the save method again, in the subclass. I guess it won't technically hurt anything, but what's the point of inheritance if you're going to repeat code, anyways? If user is not nullable, you can always add an if block to check if self.user is not None before adding it to the model. Of course, if user is not nullable, your model won't likely save without self.user anyways.
This one seems to work fine. Thanks Chris!
If this can be coded better please let me know.
class UserFormMixin(forms.ModelForm):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, commit=True):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if commit:
return obj.save()
else:
return obj
class SomeFormWithUserField(UserFormMixin):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
# self.send_mail() f.e.
return data.save()
class SomeOtherFormWithUser(UserFormMixin):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# this will work too