Override Django User model __unicode__ - django-models

Currently, Django 1.2.3 User model unicode is
def __unicode__(self):
return self.username
and I'd like to override it so its:
def __unicode__(self):
return u'%s, %s' % (self.last_name, self.first_name)
How to?
To similar effect:
User._meta.ordering = ['last_name', 'first_name']
works when defined anywhere

If you simply want to show the full name in the admin interface (which is what I needed), you can easily monkey-patch it during runtime. Just do something like this in your admin.py:
from django.contrib import admin
from django.contrib.auth.models import User
def user_unicode(self):
return u'%s, %s' % (self.last_name, self.first_name)
User.__unicode__ = user_unicode
admin.site.unregister(User)
admin.site.register(User)

Django's Proxy Model solved this problem.
This is my solution:
form.fields['students'].queryset = Student.objects.filter(id__in = school.students.all())
Here school.students is a m2m(User), Student is a proxy model of User.
class Student(User):
class Meta:
proxy = True
def __unicode__(self):
return 'what ever you want to return'
All above helps you to solve if your want to show your User ForeignKey in your custom method. If your just want to change it in admin view, there is a simple solution:
def my_unicode(self):
return 'what ever you want to return'
User.__unicode__ = my_unicode
admin.site.unregister(User)
admin.site.register(User)
add these codes to admin.py, it works.

If you need to override these, chances are you would need more customizations later on.
The cleanest practice would be using a user profile models instead of touching the User model

Create a proxy User class.
class UserProxy(User):
class Meta:
proxy = True
ordering = ['last_name', 'first_name']
def __unicode__(self):
return u'%s, %s' % (self.last_name, self.first_name)

I just found this simple method on django 1.5
def __unicode__(self):
a = self.last_name
b = self.first_name
c = a+ "-" +b
return c
it will return what you want

Related

Allowing Edit to editable=False Fields in Django Admin

DRF will use the editable=False on a field to default the Serializer to read-only. This is a very helpful / safe default that I take advantage of (ie I won't forget to set the Serializer to read-only). That being said once I have set editable=False is there any way to then force the Django admin to allow editing one of those fields?
Presumably the admin is a super user and I do want him to be able to change the fields value but fore safety I want the default Serializer logic to be read only.
UPDATE
I don't actually need to be able to edit the field as much as "set-it" when I create the object.
You are going about this the wrong way.
Your models should be the most pure implementation of the things you are modelling. If something about a model is fixed (for example a creation date) it shouldn't be editable in the model, if its mutable, then leave as editable in the model.
Otherwise, in the future you (or someone else) might be stuck wondering why a field which is set to editable=False is some how being changed. Especially as the documentation states:
If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation.
If you have one view in which it shouldn't be editable (such as in the API), then override it there.
If you have multiple serilaizers for a model, instead make an abstract serializer with a read_only_fields set and then subclass that. For example:
class AbstractFooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
read_only_fields = ('bar',)
class MainFooSerializer(AbstractFooSerializer):
pass
class DifferentFooSerializer(AbstractFooSerializer):
pass
If you really, really want to use editable=False, but allow the item to be edited in the Admin site only on creation you have an up hill battle.
Probably the best approach would be to reimplement the AdminForm you are using for the Admin
So instead of:
class FooAdmin(admin.ModelAdmin):
Use:
class FooAdmin(admin.ModelAdmin):
form = MySpecialForm
Then declare the form:
class MySpecialForm(forms.Model):
def __init__(self, *args, **kwargs):
self.is_new = False
if kwargs.get('instance',None) is None:
# There is no instance, thus its a new item
self.is_new = True
self.fields['one_time_field'] = forms.CharField() # Or what have you.
super(MySpecialForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = super(MySpecialForm, self).save(commit)
if self.is_new:
instance.your_one_time_only_field = self.one_time_field
instance.save()
return instance
Note: you will need to manually add a field and save each readonly field that you want to do this for. This may or may not be 100% functional.
For those who want to allow editing of a non-editabled field only during creation (no instance.pk, yet):
# models.py
class Entity(Model):
name = CharField(max_length=200, unique=True, null=False, blank=False, editable=False)
# admin.py
#register(Entity)
class EntityAdmin(ModelAdmin):
def get_readonly_fields(self, request, obj=None):
if obj: # This is the case when obj is already created i.e. it's an edit
return ['id', 'name']
else:
return []
# this override prevents that the new_name field shows up in the change form if it's not a creation
def get_form(self, request, obj=None, **kwargs):
orig_self_form = self.form
if not obj:
self.form = CreateEntityForm
result = super().get_form(request, obj=obj, **kwargs)
self.form = orig_self_form
return result
# forms.py
class CreateEntityForm(ModelForm):
new_name = CharField(max_length=200, min_length=2, label='Name', required=True)
def clean_new_name(self):
code = self.cleaned_data['new_name']
# validate uniqueness - if you need
exists = Entity.objects.filter(name=code).first()
if exists:
raise ValidationError('Entity with this name already exists: {}', exists)
return name
def save(self, commit=True):
if self.instance.pk:
raise NotImplementedError('Editing of existing Entity is not allowed!')
self.instance.name = self.cleaned_data['new_name'].upper()
return super().save(commit)
class Meta:
model = Entity
fields = ['new_name']
exclude = ['id', 'name']

Extending the typer of user the most simple way in Django

I want to create two types of user in Django in the most simple way.
I want to use class AbstractBaseUser
class BaseUser(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
class Service_provider(BaseUser):
company = models.CharField(max_length=140);
def __unicode__(self):
return self.company
class Customer(BaseUser):
name = models.CharField(max_length=140);
def __unicode__(self):
return self.name
I don't know how to pass this two user to the user model without applying any complicated change in the auth model.
Is it possible?
No. Django's built-in auth framework does not support more than one user model. You will need to write your own auth framework from scratch.

Django-nonrel form field for ListField

I'm experimenting with django-nonrel on appengine and trying to use a djangotoolbox.fields.ListField to implement a many-to-many relation. As I read in the documentation a ListField is something that you can use to make a workaround for djamgo-nonrel not supporting many-to-many relations.
This is an excerpt from my model:
class MyClass(models.Model):
field = ListField(models.ForeignKey(AnotherClass))
So if I am getting this right I am creating a list of foreign keys to another class to show a relationship with multiple instances of another class
With this approach everything works fine ... No Exceptions. I can create `MyClass' objects in code and views. But when I try to use the admin interface I get the following error
No form field implemented for <class 'djangotoolbox.fields.ListField'>
So I though I would try something that I haven't done before. Create my own field. Well actually my own form for editing MyClass instances in the admin interface. Here is what I did:
class MyClassForm(ModelForm):
field = fields.MultipleChoiceField(choices=AnotherClass.objects.all(), widget=FilteredSelectMultiple("verbose_name", is_stacked=False))
class Meta:
model = MyClass
then I pass MyClassForm as the form to use to the admin interface
class MyClassAdmin(admin.ModelAdmin):
form = MyClassForm
admin.site.register(MyClass, MyClassAdmin)
I though that this would work but It doesn't. When I go to the admin interface I get the same error as before. Can anyone tell what I am doing wrong here ... or if you have any other suggestions or success stories of using the ListField, SetField, etc. from djangotoolbox.fields in the admin interface it would be very much appreciated.
OK, here is what I did to get this all working ...
I'll start from the beginning
This is what what my model looked like
class MyClass(models.Model):
field = ListField(models.ForeignKey(AnotherClass))
I wanted to be able to use the admin interface to create/edit instances of this model using a multiple select widget for the list field. Therefore, I created some custom classes as follows
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(SelectMultiple):
pass
class FormListField(MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
These classes allow the listfield to be used in the admin. Then I created a form to use in the admin site
class MyClassForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyClasstForm,self).__init__(*args, **kwargs)
self.fields['field'].widget.choices = [(i.pk, i) for i in AnotherClass.objects.all()]
if self.instance.pk:
self.fields['field'].initial = self.instance.field
class Meta:
model = MyClass
After having done this I created a admin model and registered it with the admin site
class MyClassAdmin(admin.ModelAdmin):
form = MyClassForm
def __init__(self, model, admin_site):
super(MyClassAdmin,self).__init__(model, admin_site)
admin.site.register(MyClass, MyClassAdmin)
This is now working in my code. Keep in mind that this approach might not at all be well suited for google_appengine as I am not very adept at how it works and it might create inefficient queries an such.
As far as I understand, you're trying to have a M2M relationship in django-nonrel, which is not an out-of-the-box functionality. For starters, if you want a quick hack, you can go with this simple class and use a CharField to enter foreign keys manually:
class ListFormField(forms.Field):
""" A form field for being able to display a djangotoolbox.fields.ListField. """
widget = ListWidget
def clean(self, value):
return [v.strip() for v in value.split(',') if len(v.strip()) > 0]
But if you want to have a multiple selection from a list of models normally you'd have to use ModelMultipleChoiceField, which is also not functional in django-nonrel. Here's what I've done to emulate a M2M relationship using a MultipleSelectField:
Let's say you have a M2M relationship between 2 classes, SomeClass and AnotherClass respectively. You want to select the relationship on the form for SomeClass. Also I assume you want to hold the references as a ListField in SomeClass. (Naturally you want to create M2M relationships as they're explained here, to prevent exploding indexes if you're working on App Engine).
So you have your models like:
class SomeClass(models.Model):
another_class_ids = ListField(models.PositiveIntegerField(), null=True, blank=True)
#fields go here
class AnotherClass(models.Model):
#fields go here
And in your form:
class SomeClassForm(forms.ModelForm):
#Empty field, will be populated after form is initialized
#Otherwise selection list is not refreshed after new entities are created.
another_class = forms.MultipleChoiceField(required=False)
def __init__(self, *args, **kwargs):
super(SomeClassForm,self).__init__(*args, **kwargs)
self.fields['another_class'].choices = [(item.pk,item) for item in AnotherClass.objects.all()]
if self.instance.pk: #If class is saved, highlight the instances that are related
self.fields['another_class'].initial = self.instance.another_class_ids
def save(self, *args, **kwargs):
self.instance.another_class_ids = self.cleaned_data['another_class']
return super(SomeClassForm, self).save()
class Meta:
model = SomeClass
Hopefully this should get you going for the start, I implemented this functionality for normal forms, adjust it for admin panel shouldn't be that hard.
This could be unrelated but for the admin interface, be sure you have djangotoolbox listed after django.contrib.admin in the settings.. INSTALLED_APPS
You could avoid a custom form class for such usage by inquiring for the model object
class ModelListField(ListField):
def __init__(self, embedded_model=None, *args, **kwargs):
super(ModelListField, self).__init__(*args, **kwargs)
self._model = embedded_model.embedded_model
def formfield(self, **kwargs):
return FormListField(model=self._model, **kwargs)
class ListFieldWidget(SelectMultiple):
pass
class FormListField(MultipleChoiceField):
widget = ListFieldWidget
def __init__(self, model=None, *args, **kwargs):
self._model = model
super(FormListField, self).__init__(*args, **kwargs)
self.widget.choices = [(unicode(i.pk), i) for i in self._model.objects.all()]
def to_python(self, value):
return [self._model.objects.get(pk=key) for key in value]
def clean(self, value):
return value

Django-nonrel in Google App Engine ListField

I am trying to build an example app in Google App Engine using django-nonrel. and am having problems implementing ListField attribute into a model.
I have created an app test_model and have included it as an installed app in my settings. The model.py is:
from django.db import models
from djangotoolbox import *
from dbindexer import *
# Create your models here.
class Example(models.Model):
some_choices = models.ListField('Choice_examples')
notes = models.CharField(max_length='20')
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s' % (self.notes)
class Choice_examples(models.Model):
name = models.CharField(max_length='30')
def __unicode__(self):
return u'%s' % (self.name)
The above example gives me:
AttributeError:'module' object has no attribute 'Model'
If I comment out the djangotoolbox import, I get the following :
AttributeError: 'module' object has no attribute 'ListField'
What am I doing wrong here? I can't seem to find any documention as to how to go about using ListField in django-nonrel. Is that because it is supposed to really obvious?
Your imports are smashing each other:
from django.db import models
from djangotoolbox import *
The second import will replace the django.db models with djangotoolbox' empty models module. Using from X import * is a terrible idea in general in Python and produces confusing results like these.
If you're looking to use ListField from djangotoolbox, use:
from djangotoolbox import fields
and refer to the ListField class as fields.ListField.
OK, here is what I did to be able to use ListFields. MyClass the equivalent to your Example class and AnotherClass is the same as your Choice_examples. What I describe will allow you to use ListFields in the admin interface and your self implemented views.
I'll start from the beginning
This is what what my model looks like
class MyClass(models.Model):
field = ListField(models.ForeignKey(AnotherClass))
I wanted to be able to use the admin interface to create/edit instances of this model using a multiple select widget for the list field. Therefore, I created some custom classes as follows
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(SelectMultiple):
pass
class FormListField(MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
These classes allow the listfield to be used in the admin. Then I created a form to use in the admin site
class MyClassForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyClasstForm,self).__init__(*args, **kwargs)
self.fields['field'].widget.choices = [(i.pk, i) for i in AnotherClass.objects.all()]
if self.instance.pk:
self.fields['field'].initial = self.instance.field
class Meta:
model = MyClass
After having done this I created a admin model and registered it with the admin site
class MyClassAdmin(admin.ModelAdmin):
form = MyClassForm
def __init__(self, model, admin_site):
super(MyClassAdmin,self).__init__(model, admin_site)
admin.site.register(MyClass, MyClassAdmin)
This is now working in my code. Keep in mind that this approach might not at all be well suited for google_appengine as I am not very adept at how it works and it might create inefficient queries an such.
I don't know, but try with:
class Choice_examples(models.Model):
name = models.CharField(max_length='30')
def __unicode__(self):
return u'%s' % (self.name)
class Example(models.Model):
some_choices = models.ListField(Choice_examples)
notes = models.CharField(max_length='20')
updated_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s' % (self.notes)
Looks like the answer is that you cannot pass an object into fields.ListField.
I have ditched trying to work with ListField as documentation is limited and my coding skills aren't at a level for me to work it out.
Anyone else coming across a similar problem, you should consider create a new model to map the ManyToMany relationships. And if the admin view is important, you should look into the following to display the ManyToMany table inline with any given admin view:
http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#s-working-with-many-to-many-models

How to do custom display and auto-select in django admin multi-select field?

I'm new to django, so please feel free to tell me if I'm doing this incorrectly. I am trying to create a django ordering system. My order model:
class Order(models.Model):
ordered_by = models.ForeignKey(User, limit_choices_to = {'groups__name': "Managers", 'is_active': 1})
in my admin ANY user can enter an order, but ordered_by must be someone in the group "managers" (this is the behavior I want).
Now, if the logged in user happens to be a manager I want it to automatically fill in the field with that logged in user. I have accomplished this by:
class OrderAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "ordered_by":
if request.user in User.objects.filter(groups__name='Managers', is_active=1):
kwargs["initial"] = request.user.id
kwargs["empty_label"] = "-------------"
return db_field.formfield(**kwargs)
return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
This also works, but the admin puts the username as the display for the select box by default. It would be nice to have the user's real name listed. I was able to do it with this:
class UserModelMultipleChoiceField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return obj.first_name + " " + obj.last_name
class OrderForm(forms.ModelForm):
ordered_by = UserModelChoiceField(queryset=User.objects.all().filter(groups__name='Managers', is_active=1))
class OrderAdmin(admin.ModelAdmin):
form = OrderForm
My problem: I can't to both of these. If I put in the formfield_for_foreignkey function and add form = OrderForm to use my custom "UserModelChoiceField", it puts the nice name display but it won't select the currently logged in user. I'm new to this, but my guess is that when I use UserModelChoiceField it "erases" the info passed in via formfield_for_foreignkey. Do I need to use the super() function somehow to pass on this info? or something completely different?
Eliminate the ModelChoiceField/ModelMultipleChoiceField subclass completely and work off the formfield_for_foreignkey method. The request argument isn't available in the subclass, and so you can't get the current user.
Then use label_from_instance method inside formfield_for_foreignkey. You can write this yourself, but a robust Django snippet is available at http://djangosnippets.org/snippets/1642/. Just subclass the class from that snippet. You can put it in a different file and import it, or just write it above the OrderAdmin class as OrderAdmin(NiceUserModelAdmin).
Lastly, rewrite the formfield_for_foreignkey method to take the kwargs["initial"] = request.user.id outside the if statement. I don't think that's necessary and I too had trouble making it work that way.
# admin.py
from django.contrib import admin
from django.contrib.auth.models import User
from (...) import Order
class NiceUserModelAdmin(admin.ModelAdmin):
# ...
class OrderAdmin(NiceUserModelAdmin):
# ...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
kwargs["initial"] = request.user.id
if db_field.name == "ordered_by":
kwargs["empty_label"] = "-------------"
return db_field.formfield(**kwargs)
return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Resources