I would like to see the user.email instead of the user.username when print(user) is called. This is to say that in my admin, i would like to see the emails as foreign keys.
Normally i would do in the following way as described on the django tutorial:
class Poll(models.Model):
# ...
def __unicode__(self):
return self.question
However, User class is prewritten and i don't want to mod Django. How then should i proceed?
UPDATE:
I added the following to my Model:
def email(self):
u = User.object.get(pk=self.user.id)
return u.email
How do i tie it to my list_display now?
You could define a method on your Poll class called 'get_username' or something, that returns the email address of the user instead of their actual username. Then pass 'get_username' as a parameter to your 'list_display' attribute in the ModelAdmin of your Poll class.
The use-case you define requires overriding the User.__unicode__ method.
From the django docs on list_display:
If the field is a ForeignKey, Django will display the __unicode__() of the related object.
I can't see any way around this.
Related
I am creating an expense submission system, which will be multi-user.
For the purpose of this question, there are two models: Claim and Journey. A user creates a claim and each claim can have multiple journeys. I have made a gist of the code snippet as it's quite long.
In this snippet, I have sucessfully:
Made ClaimListView.get_queryset filter by current user, so whoever's logged in can only see a list of their own claims.
Made ClaimCreateView.form_valid set the correct user when the form is submitted.
Made ClaimDetailView.get_queryset filter by current user. If someone tries the url for another user's claim detail, they get a 404 (perfect!)
Done the same as above for JourneyListView
Done the same as above for JourneyDetailView - again 404 if not authroised :D
However, when I access JourneyCreateView via the URL, the dropdown box for claim still shows claims for the other users.
How should I filter the user within the JourneyCreateView class, so that the claim field only shows claims assigned to the current user?
The closest to a solution I've got is this answer which suggests overriding the __init__ function in the JourneyForm which would leave me with this:
class JourneyForm(forms.ModelForm):
class Meta:
model = Journey
fields = ['date', 'distance','claim']
def __init__(self,alloweduser,*args,**kwargs):
super (JourneyForm,self ).__init__(self,*args,**kwargs) # populates the post
self.fields['claim'].queryset = Claim.objects.filter(tech_id=alloweduser)
However I'm not sure how to pass the alloweduser in from JourneyCreateView or, more to the point, obtain the current user in this class.
form_valid isn't any use in this case, as I'm trying to obtain the user prior to the form being submitted.
In views, the request the view is handling is stored in self.request, so you can obtain the user with self.request.user, and its id with self.request.user.id.
A Django view with the FormMixin [Django-doc] has a method that can be overwritten to pass parameters: get_form_kwargs() [Django-doc].
So we can implement this as:
from django.views.generic.edit import CreateView
class JourneyCreateView(CreateView):
model = Journey
form_class = JourneyForm
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs(*args, **kwargs)
kwargs['alloweduser'] = self.request.user.id
return kwargs
# ...
I've overloaded admin form for a model by adding an extra-field
class MyModelAdminForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(), required=False)
class Meta:
model = MyModel
The password field isn't exists in model and I don't want it to be stored automatically.
I want to retrieve the value of this form field in the pre_save method :
#receiver(pre_save, sender=Member)
def my_pre_save_method(sender, **kwargs):
...
Actually I don't find a way to retrieve it.
Is this possible ? And How ?
Thanks
I don't full understand what you're asking here.
Anyhow, your question is far too general to answer in full. As an overview you only 'pass' clean form data in a view i.e. when you create an instance of that object. In your case this would be 'Member'.
I would suggest you start learning Django with the tutorials. They really do help, honest.
pre_save is called from model's save and it works on form level. The field password is not a model field so this will not be available on the instance of model and hence you can't access it in pre_save.
So, you can only retrieve is in the view using cleaned_data, and then use it in some way you want.
In a Django app, I'm having a model Bet which contains a ManyToMany relation with the User model of Django:
class Bet(models.Model):
...
participants = models.ManyToManyField(User)
User should be able to start new bets using a form. Until now, bets have exactly two participants, one of which is the user who creates the bet himself. That means in the form for the new bet you have to chose exactly one participant. The bet creator is added as participant upon saving of the form data.
I'm using a ModelForm for my NewBetForm:
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'participants': forms.Select()
}
def save(self, user):
... # save user as participant
Notice the redefined widget for the participants field which makes sure you can only choose one participant.
However, this gives me a validation error:
Enter a list of values.
I'm not really sure where this comes from. If I look at the POST data in the developer tools, it seems to be exactly the same as if I use the default widget and choose only one participant. However, it seems like the to_python() method of the ManyToManyField has its problems with this data. At least there is no User object created if I enable the Select widget.
I know I could work around this problem by excluding the participants field from the form and define it myself but it would be a lot nicer if the ModelForm's capacities could still be used (after all, it's only a widget change). Maybe I could manipulate the passed data in some way if I knew how.
Can anyone tell me what the problem is exactly and if there is a good way to solve it?
Thanks in advance!
Edit
As suggested in the comments: the (relevant) code of the view.
def new_bet(request):
if request.method == 'POST':
form = NewBetForm(request.POST)
if form.is_valid():
form.save(request.user)
... # success message and redirect
else:
form = NewBetForm()
return render(request, 'bets/new.html', {'form': form})
After digging in the Django code, I can answer my own question.
The problem is that Django's ModelForm maps ManyToManyFields in the model to ModelMultipleChoiceFields of the form. This kind of form field expects the widget object to return a sequence from its value_from_datadict() method. The default widget for ModelMultipleChoiceField (which is SelectMultiple) overrides value_from_datadict() to return a list from the user supplied data. But if I use the Select widget, the default value_from_datadict() method of the superclass is used, which simply returns a string. ModelMultipleChoiceField doesn't like that at all, hence the validation error.
To solutions I could think of:
Overriding the value_from_datadict() of Select either via inheritance or some class decorator.
Handling the m2m field manually by creating a new form field and adjusting the save() method of the ModelForm to save its data in the m2m relation.
The seconds solution seems to be less verbose, so that's what I will be going with.
I don't mean to revive a resolved question but I was working a solution like this and thought I would share my code to help others.
In j0ker's answer he lists two methods to get this to work. I used method 1. In which I borrowed the 'value_from_datadict' method from the SelectMultiple widget.
forms.py
from django.utils.datastructures import MultiValueDict, MergeDict
class M2MSelect(forms.Select):
def value_from_datadict(self, data, files, name):
if isinstance(data, (MultiValueDict, MergeDict)):
return data.getlist(name)
return data.get(name, None)
class WindowsSubnetForm(forms.ModelForm):
port_group = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=PortGroup.objects.all())
class Meta:
model = Subnet
The problem is that ManyToMany is the wrong data type for this relationship.
In a sense, the bet itself is the many-to-many relationship. It makes no sense to have the participants as a manytomanyfield. What you need is two ForeignKeys, both to User: one for the creator, one for the other user ('acceptor'?)
You can modify the submitted value before (during) validation in Form.clean_field_name. You could use this method to wrap the select's single value in a list.
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'participants': forms.Select()
}
def save(self, user):
... # save user as participant
def clean_participants(self):
data = self.cleaned_data['participants']
return [data]
I'm actually just guessing what the value proivded by the select looks like, so this might need a bit of tweaking, but I think it will work.
Here are the docs.
Inspired by #Ryan Currah I found this to be working out of the box:
class M2MSelect(forms.SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
rendered = super(M2MSelect, self).render(name, value=value, attrs=attrs, choices=choices)
return rendered.replace(u'multiple="multiple"', u'')
The first one of the many to many is displayed and when saved only the selected value is left.
I found an easyer way to do this inspired by #Ryan Currah:
You just have to override "allow_multiple_selected" attribut from SelectMultiple class
class M2MSelect(forms.SelectMultiple):
allow_multiple_selected = False
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
participants = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=User.objects.all())
I altered the User class in "/home/david/django/django/contrib/auth/models.py" as follows to override the string representation for a user in my Django application.
class User(models.Model):
...
def __unicode__(self):
return self.get_profile().full_name()
I had written a function called full_name() in my user profile model to display full names the way I want them to be displayed.
However, after I restart Apache, I find that users in select menus of model forms are still represented by usernames. Why?
Don't try to monkey patch you installation. It really is a bad idea.
You can read here how you can override the way a model choice form field shows its model instances.
In you case it would look something like this:
class UserChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.get_profile().full_name()
Then use this field in your forms. In a model form you will have to override the default field that is used.
In django models, if we have
def __unicode__(self): then it will be used as how you want to display the model by default
Now in django admin, I want to have a custmized display field(showing this object as an url so can navigate to this object), but I can't change unicode method for it used for other purpose. What do I supposed to do?
You can create a custom method for admin class
class SomeModelAdmin(admin.ModelAdmin):
list_display = ('__unicode__', 'active_status')
def active_status(self, obj):
if obj.profile.is_active:
return """One"""
return """Two"""
active_status.allow_tags = True
active_status.description = ""
This is just very simple example, so you can put your logic into this function
You can also return some html code
Don't use __unicode__ for a purpose like setting a convenience URL.
That will obscure your printed object name for any other purpose.
From which view are you trying to create a link? From the changelist view? From the change view? From a foreign key?
In general, you can simply define any method on your model (or ModelAdmin), that returns a full HTML link <a href=, set allow_tags = True, and refer to it in your admin fields.
# models method
def admin_url(self):
return 'Edit Model' % the_url
admin_url.allow_tags = True
# ModelAdmin method, through ForeignKey
def admin_url(self, obj):
return 'Edit Model' % obj.foreignkey.url
admin_url.allow_tags = True
I agree with those answers, but on my machine just not working.
I was using Python3 and Django1.8, and try to use this.
class MyModel(models.Model):
name = models.CharField(max_length=60)
def __str__(self):
return 'MyModel: {}'.format(self.name)