I have the following code:
models.py
from django.db import models
from parler.models import TranslatableModel, TranslatedFields
class Federation(TranslatableModel):
translations = TranslatedFields(
name = models.CharField('name', max_length=50)
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Athlete(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
federation = models.ForeignKey('Federation', on_delete=models.SET_NULL, null=True)
height = models.IntegerField();
weight = models.IntegerField();
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
admin.py
from django.contrib import admin
from parler.admin import TranslatableAdmin
from .models import Athlete, Federation
class AthleteAdmin(admin.ModelAdmin):
list_display = ['first_name', 'last_name', 'height', 'weight', 'get_federation_name']
fields = ['first_name', 'last_name', 'height', 'weight', 'federation']
def get_federation_name(self, obj):
obj.federation.set_current_language('en')
return obj.federation.name
get_federation_name.short_description = 'Federation'
class FederationAdmin(TranslatableAdmin):
search_fields = ['translations__name']
list_display = ['name']
fields = ['name']
admin.site.register(Federation, FederationAdmin)
admin.site.register(Athlete, AthleteAdmin)
The federation field is shown as a list but the text in the select menu is shown as "Federation object." For the list, I created a function to fetch the data from related Federation model's translation relation. I want to do the same with the form fields. If I get this to work in form fields without a function, I will also change the list display to work the same way.
I am new to Python and Django (first time) and I can't seem to find a solution to this problem.
Thank you!
By default it uses the __str__() method of the object. So the easiest way to change this is to set this method. For instance:
class Federation(models.Model):
...
def __str__(self):
return "{0} ({1})".format(self.translation, self.created_at)
Another way — if you don't want to override the __str__ method — would be to override the label_from_instance of the form Field itself. But it's more tricky.
def _federation_label_from_instance(self, obj):
return "{0} ({1})".format(obj.translation, obj.created_at)
class AthleteAdmin(admin.ModelAdmin):
...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
formfield = super().form_field(db_field, request, **kwargs)
if db_field.name == 'federation':
formfield.label_from_instance = _federation_label_from_instance
return formfield
Related
I'm trying to add the CountryField to a serializer for the Register process (using dj-rest-auth) and can't find the correct way to implement it.
All the answers I found just say to use what the documentation says, but that doesn't help for me, maybe Im just not doing it right.
This is what the documentation of django-countries says:
from django_countries.serializers import CountryFieldMixin
class CountrySerializer(CountryFieldMixin, serializers.ModelSerializer):
class Meta:
model = models.Person
fields = ('name', 'email', 'country')
I need to add the field here:
class CustomRegisterSerializer(RegisterSerializer, CountryFieldMixin):
birth_date = serializers.DateField()
country = CountryField()
gender = serializers.ChoiceField(choices=GENDER)
# class Meta:
# model = User
# fields = ('country')
# Define transaction.atomic to rollback the save operation in case of error
#transaction.atomic
def save(self, request):
user = super().save(request)
user.birth_date = self.data.get('birth_date')
user.country = self.data.get('country')
user.gender = self.data.get('gender')
user.save()
return user
User Model
class User(AbstractUser):
"""
Default custom user model
"""
name = models.CharField(max_length=30)
birth_date = models.DateField(null=True, blank=True)
country = CountryField(null=True, blank=True, blank_label='Select country')
gender = models.CharField(choices=GENDER, max_length=6, null=True, blank=True)
...
I tried different things besides this and nothing worked.
For the serializer, you import the CountryField of the django_countries.serializer_fields module, so:
from django_countries.serializer_fields import CountryField
class CustomRegisterSerializer(RegisterSerializer):
# …
country = CountryField()
# …
If you instead want to work with the Mixin (which will use such CountryField serializer field), you should specify the CountryFieldMixin before the RegisterSerializer, otherwise it will not override the .build_standard_field(…) method.
You thus inherit with:
class CustomRegisterSerializer(CountryFieldMixin, RegisterSerializer):
# …
In that case you should not specify the country serializer field manually, since that will render the mixin ineffective.
I try to do tabular inline admin.
In the child tab, if we include a ForeignKey field, it will show the str property of that foreign model.
But how to also show another property of that foreign model?
Here is my models.py
class RawMaterial(models.Model):
name = models.CharField(max_length=15)
ubuy = models.CharField(max_length=5)
usell = models.CharField(max_length=5)
uconv = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.name
class Coctail(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Ingredient(models.Model):
coctail = models.ForeignKey(Coctail,
related_name='ingredient',
on_delete=models.CASCADE)
rawmaterial = models.ForeignKey(RawMaterial,
related_name='ingredient',
on_delete=models.CASCADE)
qty = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.rawmaterial
def rawusell(self):
return self.rawmaterial.usell
rawusell.short_description = 'UOM'
Here is my admin.py
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(RawMaterial)
class IngredientInline(admin.TabularInline):
model = Ingredient
list_display = ('rawmaterial', 'qty', 'rawusell')
class CoctailAdmin(admin.ModelAdmin):
inlines = [IngredientInline]
admin.site.register(Coctail, CoctailAdmin)
and here what I got
My question is : How to show rawmaterial.usell in Ingredient tab?
Sincerely
-bino-
You can show the rawmaterial.usell field in the ingredient tab but it will not be editable. Since, any field can only be editable if they are a field of that model (Without using any custom form and logic).
So, if you want rawmaterial.usell to be editable, you will have to make a rawmaterial admin
You can show the rawmaterial.usell in IngredientInline by doing this.
class IngredientInline(admin.TabularInline):
model = Ingredient
readonly_fields = ('rawusell', )
list_display = ('rawmaterial', 'qty', 'rawusell')
def rawusell(self, obj):
return obj.rawmaterial.usell
This will start showing usell in the inline admin.
I am quite new with Django and I need help.
My problem is quite similar what Mike had in his case:
UpdateView not populating form with existing data, but I have not found solution yet.
My goal is to view owner dropdown selection list only those users who are members of the organization.
models.py
# organizations.models.py
...
from accounts.models import User
from core.models import TimeStampModel
...
class Organization(TimeStampModel, models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(
verbose_name=_('Name'),
max_length=255,
unique=True
)
code = models.CharField(
verbose_name=_('Code'),
max_length=255,
null=True,
blank=True
)
owner = models.ForeignKey(
User,
on_delete=models.PROTECT,
verbose_name=_('Owner'),
related_name='owner',
help_text=_('Organization Owner and Contact Person'),
)
slug = models.SlugField(verbose_name=_('Organization key'), unique=True)
...
class Meta:
verbose_name = _('Organization')
verbose_name_plural = _('Organization')
ordering = ['name', 'code']
def __str__(self):
return f'{self.name}, {self.code}'
# Create automatically slug value from organization name field.
# In case similar is exist then add extra count digit end of slug.
def _get_unique_slug(self):
slug = slugify(self.name)
unique_slug = slug
num = 1
while Organization.objects.filter(slug=unique_slug).exists():
unique_slug = '{}-{}'.format(slug, num)
num += 1
return unique_slug
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self._get_unique_slug()
self.next_update = timezone.now() + relativedelta(
months=self.update_interval)
super(Organization, self).save(*args, **kwargs)
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_main_page', kwargs=kwargs)
class OrganizationMembers(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
verbose_name=_('Organization')
)
member = models.ForeignKey(
User,
on_delete=models.CASCADE,
verbose_name=_('Member'),
null=True,
blank=True
)
organization_admin = models.BooleanField(
verbose_name=_('Organization admin'),
default=False
)
class Meta:
verbose_name = _('Organization: Member')
verbose_name_plural = _('Organization: Members')
ordering = ['organization', 'member']
unique_together = ('organization', 'member')
def __str__(self):
return f'{self.member}'
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_detail', kwargs=kwargs)
forms.py
# organizations.forms.py
....
from accounts.models import User
from .models import Organization, OrganizationMembers
...
class OrganizationUpdateForm(forms.ModelForm):
class Meta:
model = Organization
fields = '__all__'
exclude = ('date_created', 'created_by', 'created_by_id',
'last_updated', 'last_updated_by', 'last_updated_by_id',
'next_update', 'slug')
# Restrict user selection lists to view only members of the organization
def __init__(self, *args, **kwargs):
inst = kwargs.get('instance', None)
super(OrganizationUpdateForm, self).__init__(*args, **kwargs)
self.fields['owner'].queryset = OrganizationMembers.objects.\ # <--- !!!
filter(organization_id=inst.id)
In the forms.py, if I comment out self.field['owner]... line, then owner field will show saved value from database, but then I can see all users in the dropdown list. When queryset is enabled then selection list show correct users, but saved value is not visible.
views.py
# organizations.views.py
from .forms import OrganizationUpdateForm
from accounts.models import User
from .models import Organization, OrganizationMembers
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
model = Organization
form_class = OrganizationUpdateForm
template_name = 'organizations/organization_update.html'
success_url = reverse_lazy('organizations')
# Save data and set current user to last updated by fields
def form_valid(self, form):
object = form.save(commit=False)
object.last_updated_by = self.request.user.get_full_name()
object.last_updated_by_id = self.request.user
return super(OrganizationUpdateView, self).form_valid(form)
def get_queryset(self):
criteria1 = Q(owner=self.request.user)
criteria2 = Q(organizationmembers__member=self.request.user)
criteria3 = Q(organizationmembers__organization_admin=1)
org_list = Organization.objects.\
filter(criteria1 | (criteria2 & criteria3)).distinct()
if org_list.count() != 0:
return org_list
else:
raise Http404('You don\'t have permissions!')
In Mikes case Chiheb has commented that "With UpdateView it's a little bit tricky. So, in order to initialize your form's data, you need to do it in the view itself not in the form."
What is the reason that cannot add filter to UpdateView?
Please can someone help me to solve my problem. Thanks.
UPDATE
Not filtered. Value from database is visible
Not filtered. Dropdown list show all users in the system
Filter enabled. Value is not visible
Filter enabled. Dropdown list show correct valeus
The problem is that owner in your models is a FK to User model, but you are filtering queryset in form by OrganizationMembers. Make it the same and the problem should be gone.
I am trying to save data from my Django Admin to my database but somehow it is not happening. I have created a form in one of my apps which my admin uses.I am new to Django and any help would be greatly appreciated.
Below is the relevant code:
models.py
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django_countries.fields import CountryField
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
bio = models.TextField(max_length=1000, blank=True, null=True)
image = models.FileField()
country = CountryField()
city = models.CharField(max_length=100)
twitter = models.CharField(max_length=100, null=True, blank=True)
linkedin = models.CharField(max_length=100, null=True, blank=True)
location = models.TextField()
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Mentor(models.Model):
mentor = models.CharField(max_length=100)
mentee = models.CharField(max_length=100)
def __unicode__(self):
return self.mentee
def __str__(self):
return self.mentee
forms.py
from django import forms
from models import UserProfile, Mentor
from django_countries.fields import CountryField
from django.contrib.auth.models import User
from reports.models import Reports
class UserProfileForm(forms.ModelForm):
name = forms.CharField(max_length=100)
bio = forms.Textarea()
image = forms.FileField(label='Profile Photo')
country = CountryField(blank_label='(Select Country)')
city = forms.CharField(max_length=100)
twitter = forms.CharField(max_length=100, required=False)
linkedin = forms.CharField(max_length=100, required=False)
class Meta:
model = UserProfile
exclude = ('user',)
class MentorForm(forms.ModelForm):
mentor_choices = tuple(UserProfile.objects.filter(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentee_choices = tuple(UserProfile.objects.exclude(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentor_name = forms.ChoiceField(choices=mentor_choices)
mentee_name = forms.ChoiceField(choices=mentee_choices)
def save(self, commit=True):
mentor_name = self.cleaned_data.get('mentor_name', None)
mentor_name = self.cleaned_data.get('mentee_name', None)
return super(MentorForm, self).save(commit=commit)
class Meta:
model = Mentor
fields= ('mentor_name', 'mentee_name')
admin.py
from django.contrib import admin
from .models import UserProfile, Mentor
from.forms import MentorForm
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user',)
search_fields = ['user']
def save_model(self, request, obj, form, change):
obj.created_by = request.user
obj.save()
admin.site.register(UserProfile, UserProfileAdmin )
class MentorAdmin(admin.ModelAdmin):
list_display = ('__unicode__','mentee')
search_fields = ['mentee', 'mentor']
form = MentorForm
fieldsets = (
(None,{
'fields': ('mentor_name', 'mentee_name'),
}),
)
def save_model(self, request, obj, form, change):
super(MentorAdmin, self).save_model(request, obj, form, change)
admin.site.register(Mentor, MentorAdmin )
The UserProfile works perfectly but the Mentor form in admin doesn't save anything in the database. It creates blank entries into the database, so I know that the front and backend are talking but no data is being passed. Any help will be very helpful
def save(self, commit=True):
# here you define `mentor_name`. OK.
mentor_name = self.cleaned_data.get('mentor_name', None)
# here you redefine `mentor_name`. I guess it is a typo and should be `mentee_name`.
mentor_name = self.cleaned_data.get('mentee_name', None)
# And... you don't do anything with these variables.
return super(MentorForm, self).save(commit=commit)
This method is equivalent to:
def save(self, commit=True):
return super(MentorForm, self).save(commit=commit)
Which is equivalent to not overriding the save method at all.
And what about this?
def save_model(self, request, obj, form, change):
super(MentorAdmin, self).save_model(request, obj, form, change)
What is the purpose of overriding a method and only calling the parent method with the exact same arguments?
But the actual issue is here:
mentor_choices = tuple(UserProfile.objects.filter(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentee_choices = tuple(UserProfile.objects.exclude(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentor_name = forms.ChoiceField(choices=mentor_choices)
mentee_name = forms.ChoiceField(choices=mentee_choices)
class Meta:
model = Mentor
fields = ('mentor_name', 'mentee_name')
You use a ModelForm but none of the Mentor's fields is in fields. What are you expecting this to do other than saving a row with Mentor.mentor = None and Mentor.mentee = None. You don't even mention those fields.
And why are you using CharField for Mentor.mentor and Mentor.mentee while you likely want a foreign key.
class Mentor(models.Model):
mentor = models.ForeignKey(UserProfile, models.PROTECT,
related_name='mentees')
mentee = models.ForeignKey(UserProfile, models.PROTECT,
related_name='mentors')
class MentorForm(forms.ModelForm):
class Meta:
model = Mentor
fields = ('mentor', 'mentee')
mentor = forms.ModelChoiceField(queryset=UserProfile.objects.filter(
user__is_staff=True).order_by('name'))
mentee = forms.ModelChoiceField(queryset=UserProfile.objects.exclude(
user__is_staff=True).order_by('name'))
Or even better:
class Mentor(models.Model):
mentor = models.ForeignKey(
UserProfile, models.PROTECT, related_name='mentees',
limit_choices_to={'user__is_staff': True},
)
mentee = models.ForeignKey(
UserProfile, models.PROTECT, related_name='mentors',
limit_choices_to={'user__is_staff': False},
)
Which avoids you to create a form.
I tried to find out how to set the initial value of a ModelChoiceField, I found many answers to this question but I don't really get them. I understand that I can set "initial" when calling the form in admin.py but then a model instance is mentioned and I am lost.
This is my models.py
class Articles(models.Model):
headline = models.CharField('Rubrik', max_length=200)
category = models.CharField('Kategori', max_length=200, blank=True)
extract = models.TextField('Utdrag')
image = ImageField('Bild', upload_to='articles', blank=True, default="")
text = RichTextUploadingField('Text', blank=True, default="")
added = models.DateTimeField('Publicerad', default=timezone.now, blank=True)
updated = models.DateTimeField('Uppdaterad',auto_now=True)
frontpage = models.BooleanField('Visa på startsida', default=True)
active = models.BooleanField('Aktiv', default=False)
def save(self, *args, **kwargs):
if self.added is None:
self.added = timezone.now
super(Articles, self).save(*args, **kwargs)
def __unicode__(self):
return '%s' % (self.headline)
class Meta:
verbose_name = "artikel"
verbose_name_plural = "Artiklar"
This is my forms.py
class ArticleForm(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Menu.objects.order_by('name').filter(category=True))
This is my admin.py
class ArticlesAdmin(admin.ModelAdmin):
form = ArticleForm
list_display = ('headline','category', 'extract', 'image', 'added', 'updated', 'frontpage', 'active')
admin.site.register(Articles, ArticlesAdmin)
When I edit the article in the admin section I want the stored value of the category to be the initial value for the ModelChoiceField. Do you get what I mean?
In admin.py there should be something like:
form = ArticleForm(initial = {'category': instance.something})
*EDIT: I added ForeignKey as suggested
category = models.ForeignKey(Menu)
and admin.py looks like this:
class ArticlesAdmin(admin.ModelAdmin):
form = ArticleForm
list_display = ('headline','category', 'extract', 'image', 'added', 'updated', 'frontpage', 'active')
And now it's working as expected!
This code should work:
form = ArticleForm(initial = {'category': pk})
pk is the stored value, pk = primary key