How to work with MultipleCheckBox with Django? - python

I'm new to Django and I'm trying to make an application that registers the attendance of entrepreneurs (I'm currently working on this). There are some services that I would like to select, sometimes the same person requires more than one service per appointment. However, part of the application uses the Models and part uses the Forms, I'd like to keep the two ones separate to keep the code organized, but I have no idea how to do it, I even created a separate class just for the tuple that holds the values, but no I managed to implement, can anyone help me? Here are the codes:
models.py
from django.db import models
from django_cpf_cnpj.fields import CPFField, CNPJField
class CadastroEmpreendedor(models.Model):
ABERTURA = 'ABERTURA MEI'
ALTERACAO = 'ALTERAÇÃO CADASTRAL'
INFO = 'INFORMAÇÕES'
DAS = 'EMISSÃO DAS'
PARC = 'PARCELAMENTO'
EMISSAO_PARC = 'EMISSÃO DE PARCELA'
CODIGO = 'CÓDIGO DE ACESSO'
REGULARIZE = 'REGULARIZE'
BAIXA = 'BAIXA MEI'
CANCELADO = 'REGISTRO BAIXADO'
descricao_atendimento = (
(ABERTURA, 'FORMALIZAÇÃO'),
(ALTERACAO, 'ALTERAÇÃO CADASTRAL'),
(INFO, 'INFORMAÇÕES'),
(DAS, 'EMISSÃO DAS'),
(PARC, 'PARCELAMENTO'),
(EMISSAO_PARC, 'EMISSÃO DE PARCELA'),
(CODIGO, 'CÓDIGO DE ACESSO'),
(REGULARIZE, 'REGULARIZE'),
(BAIXA, 'BAIXA MEI'),
(CANCELADO, 'REGISTRO BAIXADO'),
)
cnpj = CNPJField('CNPJ')
cpf = CPFField('CPF')
nome = models.CharField('Nome', max_length=120)
nascimento = models.DateField()
email = models.EmailField('Email', max_length=100)
telefone_principal = models.CharField(max_length=11)
telefone_alternativo = models.CharField(max_length=11, blank=True)
descricao_atendimento
def __str__(self) -> str:
return self.nome
class DescricaoAtendimento(models.Model):
descricao = models.ForeignKey(CadastroEmpreendedor, on_delete=models.CASCADE)
forms.py
from django import forms
from .models import DescricaoAtendimento
class EmpreendedorForm(forms.ModelForm):
class Meta:
model = DescricaoAtendimento
fields = ['descricao']
widgets = {'descricao': forms.CheckboxSelectMultiple(),}
views.py
from django.shortcuts import render
from django.contrib import messages
from .forms import EmpreendedorForm
def cadastro_empreendedor(request):
if str(request.method) == 'POST':
form = EmpreendedorForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, 'Produto salvo com sucesso!')
form = EmpreendedorForm()
else:
messages.success(request, 'Erro ao salvar produto!')
else:
form = EmpreendedorForm()
context = {
'form': form
}
return render(request, 'empreendedor.html', context)
If you have any tips, I really appreciate it, I started with Django almost a month ago, so there's a long way to go.
P.S.: I integrated with PostgreSQL and in the Django administration part I can save all the fields in the DB, but I can't implement that part of the checkbox.
At this moment, I get the error:
It is impossible to add a non-nullable field 'descricao' to descricaoatendimento without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
In the template, I gonna work with bootstrap4 to create the forms. But I'd like to resolve this before. I'm still learning English, so sorry for some mistakes.

It sounds like you have many Entrepreneurs, each of which can choose many Services. This is a ManyToMany Relationship and you can create it in Django by having one model for each and creating the link between them like this
class CadastroEmpreendedor(models.Model):
...
descricao_atendimento = models.ManyToManyField(DescricaoAtendimento)
class DescricaoAtendimento(models.Model):
nome = models.CharField('Nome', max_length=120, default="unnamed service")
In this case, every object/row in DescricaoAtendimento is a service. Each entrepeneur can have many services associated with them.
This way you don't need to create a model form for DescricaoAtendimento to choose services for an entrepeneur. As it's linked to them by a manytomany relationship you can have an CadastroEmpreendedor model form with just the escricao_atendimento field and the various services become available as options.
Django handles this by creating a 'through table' which is basically a table with two fields of foreign keys, one pointing to an entrepeneur, and the other to a service. You can also create this table yourself as a through table - which is useful if you want to extend data about the relationship - eg, a begin and end date for the entrepeneur's use of a service.
The error you are getting when you migrate isn't an error, per se. It seems you created an number of DescricaoAtendimento objects and then added the descricao field later. When you then try and migrate, django wants you to provide a default value for the already existing rows, or allow the field to be empty (via blank=True in the model). You can assign a dummy value and then go back and change it in /admin later, or, if you don't have a lot of data, recreate your database and remigrate. Above I've used a default value to avoid this situation.
However, if you are dead set against extra tables, you might want to look at an extension like django multiselectfield

Related

Using ManyToManyFields() with Django

I'm building a social network where user are supposed to be able to follow each other. So I define a class user with a field: ManyToMany to stock the users that follow this user. This is what I have done in my model.py:
followings = models.ManyToManyField('self', blank=True)
This is my view.py:
#login_required
def follow_test(request):
name = request.POST.get('name', '')
user_followed = Dater.objects.get(username=name)
current_user = Dater.objects.get(id=request.user.id)
print current_user.followings # display my_app.Dater.None
current_user.followings.add(user_followed)
print current_user.followings # display my_app.Dater.None
I retrieve correctly my users (current (The one who follow someone) and the followed one) but I can't add the followed user in the set followings of the current user. Can you see something I don't do properly in my view?
followings is a manager; to show the members of that relationship, you need to call .all() on it (or another manager/queryset method like order_by).
print current_user.followings.all()

How to populate choice form from db in Django?

I can't figure out how to populate choice form from db. I know about ModelChoiceForm but the problem seems to be slightly different.
I want user to choose which sector does he work in. For example: 'Finance','Electronics' etc. which I would do simple:
SECTOR_CHOICES = (('finance',_('Finance'),
'electronics',_('Electronics')...
))
But the problem is that I want admin of the web to be able to add new choices, remove choice etc.
What came to my mind is to create a simple Model called Sector:
class Sector(models.Model):
name = models.CharField(max_length=40)
and User would have new attribute sector = models.ModelChoice(Sector).
But I'm scared what would happend when admin changes or removes a sector which is already used, and more, what if he removes it and the sector attribute is required?
How to solve this problem?
I would just override the delete_model as custom action and there check if the selected sector object is in use.
def delete_model(modeladmin, request, queryset):
for obj in queryset:
if UserModel.objects.filter(sector=obj).exists():
# do not delete, just add some message warning the admin about it
else:
obj.delete()
class UserModelAdmin(admin.ModelAdmin):
actions = [delete_model]
# ...

How to modify field rendering behaviour based on state of other fields of model in django

Let's assume that I have following models:
class ScoutBook(models.Model):
troop = models.ForeignKey('Dictionary', limit_choices_to={'type' : 'Troop'}, related_name='+', blank=True, null=True)
class Dictionary(models.Model):
name = models.CharField(max_length=CHAR_FIELD_MAX_LEN, verbose_name="Nazwa")
active = models.BooleanField(verbose_name="Aktywny")
type = models.CharField(max_length=CHAR_FIELD_MAX_LEN, choices=DICTIONARY_CHOICES)
and I want to implement following logic:
when creating ScoutBook allow users to select only active troops, and when editing allow to select active troops or allow user to leave value unchanged (even if the troop is inactive). If I use limit_choices_to = {..., 'active' = True} troop that is inactive is absent from combo box in django admin.
So to be clear: let's assume that there are four troops in this system: Troop1, Troop2 and InactiveTroop, InactiveTroop2. On model creation I would like user to be able to choose Troop1 and Troop2. If model has troop field set to InactiveTroop2, I would like user to be able to choose between InactiveTroop2, Troop1 and Troop2.
I was looking at the django forms api and I didn't found obvious way do this. Moreover, in the application I'm developing there will be many such fields and many such models --- so solution must be pain free. I would rather not create new Form class for every model. I will be using mostly django admin to enable editing the database, and some read only views that will just list entries.
Ideally I would like to encapsulate this functionality in some custom field --- but fields have access to model instance on validate and save phase --- so I dont have access to it when I produce formfield.
This sounds like something you want to do in a form, not in the object itself. Create a ModelForm and override the ModelChoiceField like this:
from django import forms
class ScoutBookForm(forms.ModelForm):
troop = forms.ModelChoiceField(queryset=Troop.objects.filter(active=True))
class Meta:
model = ScoutBook
You can also override the clean method of ScoutBook to ensure it cannot ever be saved with an inactive Troop, though that may have some unintended consequences (e.g., you wouldn't be able to update a ScoutBook in the admin if the troop had gone inactive at some point in the past).
Well I had to hook into ModelForm creation. Attached Form inspects it's fields and if specific conditions are met it replaces model field queryset.
class DictionayModelForm(ModelForm):
def __init__(self, *largs, **kwargs):
super(DictionayModelForm, self).__init__(*largs, **kwargs)
if self.instance and self.instance.pk is not None:
for f in self.instance._meta.fields:
if isinstance(f, models.ForeignKey) and issubclass(f.rel.to, Dictionary):
model_field = self.fields[f.name]
value = getattr(self.instance, f.name, None)
if value and value not in model_field.choices:
model_field.queryset = Dictionary.objects.filter(Q(**f.rel.limit_choices_to) | Q(id = value.id))

Inline-like solution for Django Admin where Admin contains ForeignKey to other model

I have several Customers who book Appointments. Each Appointment has exactly one customer, though a customer can be booked for multiple appointments occurring at different times.
class Customer(model.Model):
def __unicode__(self):
return u'%s' % (self.name,)
name = models.CharField(max_length=30)
# and about ten other fields I'd like to see from the admin view.
class Appointment(models.Model):
datetime = models.DateTimeField()
customer = models.ForeignKey("Customer")
class Meta:
ordering = ('datetime',)
Now when an admin goes to browse through the schedule by looking at the Appointments (ordered by time) in the admin, sometimes they want to see information about the customer who has a certain appointment. Right now, they'd have to remember the customer's name, navigate from the Appointment to the Customer admin page, find the remembered Customer, and only then could browse their information.
Ideally something like an admin inline would be great. However, I can only seem to make a CustomerInline on the Appointment admin page if Customer had a ForeignKey("Appointment"). (Django specifically gives me an error saying Customer has no ForeignKey to Appointment). Does anyone know of a similar functionality, but when Appointment has a ForeignKey('Customer')?
Note: I simplified the models; the actual Customer field currently has about ~10 fields besides the name (some free text), so it would be impractical to put all the information in the __unicode__.
There is no easy way to do this with django. The inlines are designed to follow relationships backwards.
Potentially the best substitute would be to provide a link to the user object. In the list view this is pretty trivial:
Add a method to your appointment model like:
def customer_admin_link(self):
return 'Customer' % reverse('admin:app_label_customer_change %s') % self.id
customer_admin_link.allow_tags = True
customer_admin_link.short_description = 'Customer'
Then in your ModelAdmin add:
list_display = (..., 'customer_admin_link', ...)
Another solution to get exactly what you're looking for at the cost of being a bit more complex would be to define a custom admin template. If you do that you can basically do anything. Here is a guide I've used before to explain:
http://www.unessa.net/en/hoyci/2006/12/custom-admin-templates/
Basically copy the change form from the django source and add code to display the customer information.
Completing #John's answer from above - define what you would like to see on the your changelist:
return '%s' % (
reverse('admin:applabel_customer_change', (self.customer.id,)),
self.customer.name # add more stuff here
)
And to add this to the change form, see: Add custom html between two model fields in Django admin's change_form
In the ModelAdmin class for your Appointments, you should declare the following method:
class MySuperModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if obj:
# create your own model admin instance here, because you will have the Customer's
# id so you know which instance to fetch
# something like the following
inline_instance = MyModelAdminInline(self.model, self.admin_site)
self.inline_instances = [inline_instance]
return super(MySuperModelAdmin, self).get_form(request, obj, **kwargs)
For more information, browser the source for that function to give you an idea of what you will have access to.
https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L423
There is a library you can use it.
https://github.com/daniyalzade/django_reverse_admin
But if you want to use link to object in showing table you can like this code:
def customer_link(self, obj):
if obj.customer:
reverse_link = 'admin:%s_%s_change' % (
obj.customer._meta.app_label, obj.customer._meta.model_name)
link = reverse(reverse_link, args=[obj.customer.id])
return format_html('More detail' % link)
return format_html('<span >-</span>')
customer_link.allow_tags = True
customer_link.short_description = 'Customer Info'
And in list_display:
list_display = (...,customer_link,...)

Django: multiple models in one template using forms [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm building a support ticket tracking app and have a few models I'd like to create from one page. Tickets belong to a Customer via a ForeignKey. Notes belong to Tickets via a ForeignKey as well. I'd like to have the option of selecting a Customer (that's a whole separate project) OR creating a new Customer, then creating a Ticket and finally creating a Note assigned to the new ticket.
Since I'm fairly new to Django, I tend to work iteratively, trying out new features each time. I've played with ModelForms but I want to hide some of the fields and do some complex validation. It seems like the level of control I'm looking for either requires formsets or doing everything by hand, complete with a tedious, hand-coded template page, which I'm trying to avoid.
Is there some lovely feature I'm missing? Does someone have a good reference or example for using formsets? I spent a whole weekend on the API docs for them and I'm still clueless. Is it a design issue if I break down and hand-code everything?
This really isn't too hard to implement with ModelForms. So lets say you have Forms A, B, and C. You print out each of the forms and the page and now you need to handle the POST.
if request.POST():
a_valid = formA.is_valid()
b_valid = formB.is_valid()
c_valid = formC.is_valid()
# we do this since 'and' short circuits and we want to check to whole page for form errors
if a_valid and b_valid and c_valid:
a = formA.save()
b = formB.save(commit=False)
c = formC.save(commit=False)
b.foreignkeytoA = a
b.save()
c.foreignkeytoB = b
c.save()
Here are the docs for custom validation.
I just was in about the same situation a day ago, and here are my 2 cents:
1) I found arguably the shortest and most concise demonstration of multiple model entry in single form here: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .
In a nutshell: Make a form for each model, submit them both to template in a single <form>, using prefix keyarg and have the view handle validation. If there is dependency, just make sure you save the "parent"
model before dependant, and use parent's ID for foreign key before commiting save of "child" model. The link has the demo.
2) Maybe formsets can be beaten into doing this, but as far as I delved in, formsets are primarily for entering multiples of the same model, which may be optionally tied to another model/models by foreign keys. However, there seem to be no default option for entering more than one model's data and that's not what formset seems to be meant for.
I very recently had the some problem and just figured out how to do this.
Assuming you have three classes, Primary, B, C and that B,C have a foreign key to primary
class PrimaryForm(ModelForm):
class Meta:
model = Primary
class BForm(ModelForm):
class Meta:
model = B
exclude = ('primary',)
class CForm(ModelForm):
class Meta:
model = C
exclude = ('primary',)
def generateView(request):
if request.method == 'POST': # If the form has been submitted...
primary_form = PrimaryForm(request.POST, prefix = "primary")
b_form = BForm(request.POST, prefix = "b")
c_form = CForm(request.POST, prefix = "c")
if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
print "all validation passed"
primary = primary_form.save()
b_form.cleaned_data["primary"] = primary
b = b_form.save()
c_form.cleaned_data["primary"] = primary
c = c_form.save()
return HttpResponseRedirect("/viewer/%s/" % (primary.name))
else:
print "failed"
else:
primary_form = PrimaryForm(prefix = "primary")
b_form = BForm(prefix = "b")
c_form = Form(prefix = "c")
return render_to_response('multi_model.html', {
'primary_form': primary_form,
'b_form': b_form,
'c_form': c_form,
})
This method should allow you to do whatever validation you require, as well as generating all three objects on the same page. I have also used javascript and hidden fields to allow the generation of multiple B,C objects on the same page.
The MultiModelForm from django-betterforms is a convenient wrapper to do what is described in Gnudiff's answer. It wraps regular ModelForms in a single class which is transparently (at least for basic usage) used as a single form. I've copied an example from their docs below.
# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile
User = get_user_model()
class UserEditForm(forms.ModelForm):
class Meta:
fields = ('email',)
class UserProfileForm(forms.ModelForm):
class Meta:
fields = ('favorite_color',)
class UserEditMultiForm(MultiModelForm):
form_classes = {
'user': UserEditForm,
'profile': UserProfileForm,
}
# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm
User = get_user_model()
class UserSignupView(UpdateView):
model = User
form_class = UserEditMultiForm
success_url = reverse_lazy('home')
def get_form_kwargs(self):
kwargs = super(UserSignupView, self).get_form_kwargs()
kwargs.update(instance={
'user': self.object,
'profile': self.object.profile,
})
return kwargs
I currently have a workaround functional (it passes my unit tests). It is a good solution to my opinion when you only want to add a limited number of fields from other models.
Am I missing something here ?
class UserProfileForm(ModelForm):
def __init__(self, instance=None, *args, **kwargs):
# Add these fields from the user object
_fields = ('first_name', 'last_name', 'email',)
# Retrieve initial (current) data from the user object
_initial = model_to_dict(instance.user, _fields) if instance is not None else {}
# Pass the initial data to the base
super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
# Retrieve the fields from the user model and update the fields with it
self.fields.update(fields_for_model(User, _fields))
class Meta:
model = UserProfile
exclude = ('user',)
def save(self, *args, **kwargs):
u = self.instance.user
u.first_name = self.cleaned_data['first_name']
u.last_name = self.cleaned_data['last_name']
u.email = self.cleaned_data['email']
u.save()
profile = super(UserProfileForm, self).save(*args,**kwargs)
return profile
"I want to hide some of the fields and do some complex validation."
I start with the built-in admin interface.
Build the ModelForm to show the desired fields.
Extend the Form with the validation rules within the form. Usually this is a clean method.
Be sure this part works reasonably well.
Once this is done, you can move away from the built-in admin interface.
Then you can fool around with multiple, partially related forms on a single web page. This is a bunch of template stuff to present all the forms on a single page.
Then you have to write the view function to read and validated the various form things and do the various object saves().
"Is it a design issue if I break down and hand-code everything?" No, it's just a lot of time for not much benefit.
According to Django documentation, inline formsets are for this purpose:
"Inline formsets is a small abstraction layer on top of model formsets. These simplify the case of working with related objects via a foreign key".
See https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

Categories

Resources