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.
Related
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've got a project where the user can create a model Parceiros and in that model is a model linking all the Servicos to that model Parceiros. The problem is that the Parceiros should be linked to the current user as the only way to create a Parceiros object is logging in before. After that the only user that should be able to change the Parceiros fields or Servicos should be the current user that created o Parceiros object.
I've read some questions and tried with context processors, but didn't manage to get the Parceiros linked to the user.
services/models.py
from django.db import models
from phone_field import PhoneField
from datetime import datetime
from django.contrib.auth import get_user_model
from django.template.defaultfilters import slugify
User = get_user_model()
class Parceiros (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
nome = models.CharField(max_length=200)
endereco = models.TextField(max_length=400, blank=True)
responsavel = models.CharField(max_length=100)
tel = PhoneField(max_length=12)
created_at = models.DateTimeField(auto_now=True)
updated_at = models.DateTimeField(auto_now_add=True, blank=True)
ativo = models.BooleanField(default=False)
class Servicos (models.Model):
tipo = models.CharField(max_length=200)
objetivo = models.TextField(max_length=500, blank=True)
parceiro = models.ForeignKey(Parceiros, on_delete=models.CASCADE)
preco = models.DecimalField(max_digits=9, decimal_places=2, blank=True)
telefone = PhoneField(max_length=12, default='21968151502')
def get_image_filename(instance, filename):
tipo = instance.services.tipo
slug = slugify(tipo)
return "servicos_imagens/%s-%s" % (slug, filename)
class Imagens (models.Model):
servicos = models.ForeignKey(Servicos, on_delete=models.CASCADE)
imagem = models.ImageField(upload_to=get_image_filename)
services/views.py
from django.shortcuts import render, redirect
from .models import Servicos, Parceiros, Imagens
from django.views.generic import UpdateView, DetailView, ListView
from .forms import ParceirosForm, ServicosForm, ImagensForm
from django.contrib.auth.decorators import login_required
from django.contrib.auth.context_processors import auth
def home_view(request):
serv = Servicos.objects.all()
context = {'serv': serv }
return render (request, 'home.html', context)
#login_required
def parceiros_create(request):
if request.method =='POST':
form = ParceirosForm(request.POST)
Parceiros.user = auth.user
if form.is_valid():
parceiro = form.save(commit=False)
parceiro.save()
return redirect('home2')
else:
form = ParceirosForm()
context = {
'form': form,
}
return render (request, 'parceiroform.html', context)
def parceirosview(request):
user = Servicos.parceiro
serv = Servicos.objects.get(parceiro=user)
context = {'serv': serv}
return render(request, 'parceiro.html', context)
class ServicoView(DetailView):
model = Servicos
class ServicoUpdate(UpdateView):
model = Servicos
template_name = 'servicoform.html'
services/forms.py:
from django import forms
from .models import Servicos, Imagens, Parceiros
from phone_field import PhoneField
class ParceirosForm(forms.ModelForm):
class Meta:
prefix = 'parceiro'
model = Parceiros
fields = ['nome', 'endereco', 'responsavel', 'tel']
class ServicosForm(forms.ModelForm):
tipo = forms.CharField(max_length=200)
objetivo = forms.CharField(max_length=500)
preco = forms.DecimalField(max_digits=9, decimal_places=2)
telefone = PhoneField(max_length=12)
class Meta:
prefix = 'service'
model = Servicos
fields = ['tipo', 'objetivo', 'preco', 'telefone']
class ImagensForm(forms.ModelForm):
imagem = forms.ImageField(label='image')
class Meta:
model = Imagens
fields = ['imagem']
Parceiros is the class, so Parceiros.user = auth.user doesn't do anything.
When saving your form, you should assign the user to the actual instance of the model you're saving:
if form.is_valid():
parceiro = form.save(commit=False)
parceiro.user = request.user # assuming user is a FK field on Parceiros
parceiro.save()
Note that you should use request.user (not auth.user) which is the currently logged in user.
For the UpdateViews, you only need to change the queryset in order to ensure that only instances belonging to that user can be changed:
# inside class ServicoUpdate
# Servico is related to User via the Parceiros model
def get_queryset(self):
return super().get_queryset().filter(parceiro__user=self.request.user)
# inside class ParceiroUpdate
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
And similarly for any other view that requires to restrict access to only the current logged in user's instances, if using a class-based view, override get_queryset().
I have working models, forms, views and urls for a django CRUD app managing customer functions for a business. I just cant seem to figure out how to write a view to allow a user to add comments, or other data related to the customer and stored in other models using a single view and template.
So for example; for customer a, all the comments for customer a with the option to add, amend etc.. and the same for the other related models.
I understand how to do it for one I will be able to make quick progress. (old school programmer here)
Here is what I am working with - keeping it simple.
MODELS
class Emergency(models.Model):
# Fields
name = CharField(null = False, blank = False, max_length=60)
address = TextField(blank=True, null=True, help_text='Street and town', verbose_name='Address')
telephone = CharField(blank=False, null=False, unique= True, max_length=20)
relationship = CharField(choices=(('P', 'Parent'),('S', 'Son'),('D', 'Daughter'),('R', 'Relative'),('L', 'Partner')),max_length = 1,default='R')
class Meta:
ordering = ('-pk',)
def __unicode__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('conform_emergency_detail', args=(self.pk,))
def get_update_url(self):
return reverse('conform_emergency_update', args=(self.pk,))
class Client(models.Model):
# Fields
surname = CharField(null = False, blank = False, max_length=30)
name = CharField(null = False, blank = False, max_length=60)
# Relationship Fields
emergencycontact = models.ForeignKey(Emergency, on_delete=models.CASCADE, name = 'Emergency Contact')
class Meta:
ordering = ('-pk',)
def __unicode__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('conform_client_detail', args=(self.pk,))
def get_update_url(self):
return reverse('conform_client_update', args=(self.pk,))
class Clientnotes(models.Model):
# Fields
slug = AutoSlugField(populate_from='name', blank=True)
created = DateTimeField(auto_now_add=True, editable=False)
last_updated = DateTimeField(auto_now=True, editable=False)
note = CharField(blank=False, null=False, max_length= 300 )
# Relationship Fields
modified_by = models.ForeignKey(User, related_name='clientnotes_modified_by', on_delete=models.CASCADE, name= 'Changed by')
clientnotes = models.ManyToManyField(Client, name = 'Clients notes')
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('conform_clientnotes_detail', args=(self.slug,))
def get_update_url(self):
return reverse('conform_clientnotes_update', args=(self.slug,))
FORMS
class ClientForm(forms.ModelForm):
class Meta:
model = Client
fields = ['surname', 'name']
class ClientnotesForm(forms.ModelForm):
class Meta:
model = Clientnotes
readonly_fields = ['slug', 'modified_by']
fields = ['note']
VIEWS
class ClientListView(ListView):
model = Client
class ClientCreateView(CreateView):
model = Client
form_class = ClientForm
class ClientDetailView(DetailView):
model = Client
class ClientUpdateView(UpdateView):
model = Client
form_class = ClientForm
TEMPLATE NAMES
client_detail.html
client_form.html
client_list.html
I have simple views, forms and templates to list, view detail and add and it all works well - with the exception of related models because i am not able to add both models at the same time. I need a simple clear simpletons guide with what i have provided so it clicks into place.
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
I'm trying to save some form data inputted by the user. I would like to slugify the "name" which was entered by the user, but dont want the slug field to show on the template that the user sees. I tried to do it manually with the sell function that you see below, but cant quite get it to work. I want to eventually save the slugified name into the Item model I have listed below. I'm sure there's a much smarter/simpler way than the sell function I'm currently using :P. Thanks in advance!
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=75)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True)
image = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
class AddItem(forms.ModelForm):
class Meta:
model = Item
exclude = ('user','slug','is_active',)
def sell(request):
if request.method == "POST":
form = AddItem(request.POST)
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(form.name) **#not sure what this line should be?**
item.save()
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks.html')
else:
url = urlresolvers.reverse('register')
return HttpResponseRedirect(url)
You can exclude slug from user form.
And slugify in pre_save signal.
from django.dispatch import receiver
from django.db.models.signals import pre_save
#receiver(pre_save, sender=Item)
def iter_pre_save_handler(sender, instance, **kwargs):
if not instance.pk:
instance.slug = slugify(instance.name)
According to the docs, you can exclude a field from being rendered in a model form like this:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
or
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ('birth_date',)
or by setting editable=False on the Field instance in your model.
Once you have done this, you can override the save method of the model, as the comments in the OP have suggested:
# shamelessly copied from http://stackoverflow.com/questions/837828/how-do-i-create-a-slug-in-django/837835#837835
from django.template.defaultfilters import slugify
class test(models.Model):
q = models.CharField(max_length=30)
s = models.SlugField()
def save(self, *args, **kwargs):
self.s = slugify(self.q)
super(test, self).save(*args, **kwargs)