Custom render modelformset in django - django

I have two models with an OneToMany relationship
models:
class Proveedor(models.Model):
id_proveedor = models.AutoField(primary_key = True)
nombre = models.CharField(max_length=150,blank=False,null=False)
codigo_asignado = models.CharField(max_length=50,blank=False,null=False)
class Meta:
verbose_name_plural = "Proveedor"
def __unicode__(self):
return self.nombre
class Codigo_Corto(models.Model):
id_codigo = models.AutoField(primary_key=True)
id_proveedor = models.ForeignKey(Proveedor,db_column='id_proveedor', verbose_name='Proveedor')
codigo = models.CharField(max_length=15)
tarifa = models.IntegerField()
revenue = models.IntegerField()
moneda = models.CharField(max_length=2)
direccion = models.CharField(max_length=2,blank=True,null=True)
I want to create a template with one only choicefield widget to select the provider (proveedor) and an html where the user type remaining fields from the model "Codigo_Corto"
Forms:
class CodigoCortoForm(forms.ModelForm):
id_proveedor = forms.ModelChoiceField(queryset=Proveedor.objects.all().order_by('nombre'),required=True,label='Proveedor')
codigo = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:80px'}))
tarifa = forms.DecimalField(widget=forms.NumberInput(attrs={'style': 'width:80px'}),required=True)
revenue = forms.DecimalField(widget=forms.NumberInput(attrs={'style': 'width:80px'}),required=True)
moneda = forms.ChoiceField(
choices = (
('$',"$"),('L',"L"),
),
widget = forms.Select(attrs={'style': 'width:50px'})
,required=True )
direccion = forms.ChoiceField(
choices = (
('MT',"MT"),('MO',"MO"),('nn',"Sin Direccion"),
),
widget = forms.Select(attrs={'style': 'width:120px'})
)
def __init__(self,*args,**kwargs):
super(CodigoCortoForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
class Meta:
model = Codigo_Corto
My view:
def CCView(request):
if request.method == 'POST':
form = ccformset(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/home/')
else:
ccformset = formset_factory(CodigoCortoForm)
form = ccformset()
return render_to_response("codcorto.html",
{'form':form},
context_instance=RequestContext(request))
This way I get an id_proveedor choifield for every row in the tabe, but I need only one id_proveedor modelchoicefield outside the table.
Or I need to render a modelchoicefield for the id_proveedor separately and pass it to the formset.
Any advice
Thanks in advance

Related

apply a filter to the choices field of my model

Hi I have problems with some filters in django.
I have my own view where I can choose the day of the week which is a select choice field
once chosen it is saved in the db.
I would like to filter those already present so as not to repeat them if I had to choose another day.
Can anyone help me out please?
models.py
class Piano(models.Model):
nome_piano = models.CharField(max_length=100)
data_inizio = models.DateField()
data_fine = models.DateField()
utente_piano = models.ForeignKey(
User,
on_delete = models.CASCADE,
related_name = 'utente_piano'
)
def __str__(self):
return self.nome_piano
class Meta:
verbose_name = "Piano alimentare"
verbose_name_plural = "Piani alimentari"
class PianoSingleDay(models.Model):
giorni_settimana_scelta = [
("1","Lunedì"),
("2","Martedì"),
("3","Mercoledì"),
("4","Giovedì"),
("5","Venerdì"),
("6","Sabato"),
("7","Domenica")
]
giorni_settimana = models.CharField(
choices = giorni_settimana_scelta,
max_length = 300
)
single_piano = models.ForeignKey(
Piano,
on_delete = models.CASCADE,
related_name = 'single_piano'
)
def __str__(self):
return self.giorni_settimana
class Meta:
verbose_name = "Piano singolo"
verbose_name_plural = "Piani singoli"
views.py
#login_required
def PianoSingleView(request, id):
piano = get_object_or_404(models.Piano, id = id, utente_piano = request.user)
if request.method == 'POST':
giorno_form = PianoSingleDayForm(request.POST, prefix = 'giorno')
if giorno_form.is_valid():
day_piano = giorno_form.save(commit = False)
day_piano.single_piano = piano
day_piano.save()
return redirect('gestione-piano', id = piano.id)
else:
giorno_form = PianoSingleDayForm(prefix = 'giorno')
context = {'piano': piano, 'giorno_form': giorno_form}
return render(request, 'crud/create/gestione_piano_single.html', context)
forms.py
class PianoSingleDayForm(forms.ModelForm):
class Meta:
model = models.PianoSingleDay
exclude = ['single_piano']
1
2
You can let the PianoSingleDayForm exclude the days that have already been selected for that Piano with:
class PianoSingleDayForm(forms.ModelForm):
def __init__(self, *args, piano=None, **kwargs):
super().__init__(*args, **kwargs)
if piano is not None:
days = set(PianoDaySingle.objects.filter(
single_piano=piano
).values_list('giorni_settimana', flat=True))
self.fields['giorni_settimana'].choices = [
(k, v)
for k, v in self.fields['giorni_settimana'].choices
if k not in days
]
class Meta:
model = models.PianoSingleDay
exclude = ['single_piano']
We can then use this in the view by passing the Piano object to the form both in the GET and POST codepath:
#login_required
def PianoSingleView(request, id):
piano = get_object_or_404(models.Piano, id=id, utente_piano=request.user)
if request.method == 'POST':
giorno_form = PianoSingleDayForm(request.POST, piano=piano, prefix='giorno')
if giorno_form.is_valid():
giorno_form.instance.single_piano = piano
giorno_form.save()
return redirect('gestione-piano', id=piano.id)
else:
giorno_form = PianoSingleDayForm(piano=piano, prefix='giorno')
context = {'piano': piano, 'giorno_form': giorno_form}
return render(request, 'crud/create/gestione_piano_single.html', context)

How to validate if a field is not blank in a form when it's set Blank = True in the model

I have a model which is filled in different steps by different forms. Fields that are not filled in the first step need to be set Blank = True so you can submit the form. When I try to fill those fields later, the form lets the user leave them blank, which is undesirable. How can I make them mandatory in the subsequent forms?
I've tried implementing a Validation method (clean_almacen) like the one below, but it does nothing.
class RecepcionForm(ModelForm):
def clean_almacen(self):
data = self.cleaned_data['almacen']
# Check if the field is empty.
if data == '':
raise ValidationError(_('¡Seleccione un almacén!'))
return data
def clean_usuario(self):
if not self.cleaned_data['usuario_recepcion']:
return User()
return self.cleaned_data['usuario_recepcion']
class Meta:
model = Pedido
fields = ['almacen']
Also, setting the field Blank = False and null = True will make this work, but it will make mandatory to assign a value to the field when you edit the object in the admin page (which is undesirable too).
This is my code:
models.py
class Pedido(models.Model):
nombre = models.CharField(max_length=40, help_text=_('Nombre del producto.'))
referencia = models.CharField(max_length=20, help_text=_('Referencia del fabricante.'))
cpm = models.CharField(max_length=20, default ='A la espera.',help_text=_('Código del CPM asignado a este pedido.'), null = True, blank = True, verbose_name = _('CPM'))
fecha = models.DateTimeField(auto_now_add=True)
fecha_cpm = models.DateTimeField(blank=True, null=True, verbose_name=_('Fecha asignación CPM'))
unidades = models.IntegerField(default = 1)
usuario = models.ForeignKey(User, on_delete=models.CASCADE, blank = True)
autogestion = models.BooleanField(default = False, verbose_name='Autogestión', help_text = _("Marca esta casilla si vas a procesar tú mismo el pedido."))
usuario_recepcion = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='recepcion', verbose_name=_('Recepcionado por'))
fecha_recepcion = models.DateTimeField(blank=True, null=True)
ESTADO_PEDIDO = (
('n', _('Pendiente')),
('p', _('Proforma solicitada')),
('c', _('CPM solicitado')),
('v', _('Para validar')),
('r', _('Recibido')),
('b', _('Bloqueado')),
)
estado = models.CharField(
max_length=1,
choices=ESTADO_PEDIDO,
blank=False,
default='n',
help_text=_('Estado del pedido'),
)
fabricante = models.ForeignKey('Fabricante', null = True, on_delete=models.SET_NULL)
centro_gasto = models.ForeignKey('CentroGasto', null = True, on_delete=models.SET_NULL, verbose_name = _('Centro de Gasto'))
almacen = models.ForeignKey('Almacen', null = True, on_delete=models.SET_NULL, blank = True)
direccion = models.ForeignKey('Direccion', default = 'CIBM', null = True, on_delete=models.SET_NULL, verbose_name = _('Dirección de entrega'))
codigo = models.CharField(max_length=20, blank=True, default=keygen())
bloqueo = models.TextField(blank=True, verbose_name=_('Incidencias'), help_text = _('Anote las incidencias relacionadas con el pedido para que puedan ser solucionadas'))
views.py
#permission_required('gestion.puede_editar_cpm')
def añadir_cpm(request, pk):
instance = get_object_or_404(Pedido, id=pk)
if request.method == "POST":
form = CPMForm(request.POST, instance=instance)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.estado = 'v'
model_instance.fecha_cpm = datetime.now()
model_instance.save(update_fields=['estado', 'fecha_cpm', 'cpm'])
return redirect('/')
else:
form = CPMForm()
return render(request, "gestion/cpm_edit.html", {'form': form})
#permission_required('gestion.puede_editar_cpm')
def cpm_block(request, pk):
instance = get_object_or_404(Pedido, id=pk)
if request.method == "POST":
form = CPMBlockForm(request.POST, instance=instance)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.estado = 'b'
model_instance.save(update_fields=['estado', 'bloqueo'])
return redirect('/')
else:
form = CPMBlockForm()
return render(request, "gestion/cpm_block.html", {'form': form})
#login_required
def recepcion(request, pk):
instance = get_object_or_404(Pedido, id=pk)
if request.method == "POST":
form = RecepcionForm(request.POST, instance=instance)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.usuario_recepcion = request.user
model_instance.estado = 'r'
model_instance.fecha_recepcion = datetime.now()
model_instance.save(update_fields=['usuario_recepcion', 'almacen', 'fecha_recepcion', 'estado'])
return redirect('/')
else:
form = RecepcionForm()
return render(request, "gestion/pedido_recepcionar.html", {'form': form})
forms.py
class PedidoForm(ModelForm):
def clean_usuario(self):
if not self.cleaned_data['usuario']:
return User()
return self.cleaned_data['usuario']
class Meta:
model = Pedido
exclude = ['codigo', 'fecha', 'cpm', 'almacen', 'estado', 'usuario']
class RecepcionForm(ModelForm):
def clean_almacen(self):
data = self.cleaned_data['almacen']
# Check if a date is not in the past.
if data == '':
raise ValidationError(_('¡Seleccione un almacén!'))
return data
def clean_usuario(self):
if not self.cleaned_data['usuario_recepcion']:
return User()
return self.cleaned_data['usuario_recepcion']
class Meta:
model = Pedido
fields = ['almacen']
class CPMForm(ModelForm):
class Meta:
model = Pedido
fields = ['cpm']
class CPMBlockForm(ModelForm):
class Meta:
model = Pedido
fields = ['bloqueo']
I'm sorry for the long code, I don't know what could be useful or not. I hope you guys can help me.
Thanks in advance.
You would override the field definitions in the subsequent forms. You can do this declaratively:
class CPMForm(ModelForm):
cpm = forms.CharField(required=True, max_length=20, initial='A la espera.', help_text=_('Código del CPM asignado a este pedido.'), label=_('CPM'))
class Meta:
model = Pedido
fields = ['cpm']
or programmatically:
class CPMBlockForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['bloqueo'].required = True
class Meta:
model = Pedido
fields = ['bloqueo']

form.is_valid method keeps failing

I'm trying to make an editing page for the users to update an object data. However, form.is_valid() keeps failing, I have no idea why.
My model:
class Thread(models.Model):
title = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey(User, blank=True, null=True)
body = models.TextField(max_length=10000)
USER_TYPES = (
('INI','Iniciante'),
('INT','Intermediário'),
('AVA','Avançado')
)
user_type = models.CharField(max_length=20, choices = USER_TYPES, default='INI')
category = models.ForeignKey(Category)
orcamento = models.IntegerField(default=0)
slug = models.SlugField(max_length=40, unique=True)
def get_absolute_url(self):
return "/%s/" % self.slug
def __str__(self):
return self.title
def save(self, **kwargs):
slug_str = "%s %s" % (self.category, self.title)
unique_slugify(self, slug_str)
super(Thread, self).save(**kwargs)
My view:
def edit_thread(request, thread_slug):
thread = Thread.objects.get(slug=thread_slug)
if request.method == 'POST':
form = EditThread(request.POST)
if form.is_valid():
thread.title = form.cleaned_data['title']
thread.orcamento = form.cleaned_data['orcamento']
thread.user_type = form.cleaned_data['experiencia']
thread.body = form.cleaned_data['pergunta']
thread.save()
return HttpResponseRedirect('/thread' + thread.get_absolute_url())
else:
data = {'title' : thread.title, 'experiencia':thread.user_type, 'orcamento' : thread.orcamento, 'pergunta': thread.body}
form = EditThread(initial=data)
return render(request, 'edit_thread.html', {
'form': form })
My form:
class EditThread(forms.ModelForm):
title = forms.CharField(label='Título', max_length=200, error_messages=my_default_errors)
orcamento = forms.IntegerField(label='Preço máximo', error_messages=my_default_errors)
experiencia = forms.ChoiceField(label='Você é um usuário...', choices=Thread.USER_TYPES, error_messages=my_default_errors)
pergunta = forms.CharField(label='Pergunta', widget=forms.Textarea, error_messages=my_default_errors)
class Meta:
model = Thread
def __init__(self, *args, **kwargs):
super(EditThread, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.layout = Layout(
Div('title',
'experiencia',
PrependedAppendedText('orcamento', 'R$', ',00', active=True),
'pergunta',
FormActions(
Submit('save', 'Salvar alterações'),
)))
When accessing the page, the form gets pre-populated with the object's data as it should.
Your form should be inherited from the simple forms.Form instead of the forms.ModelForm:
class EditThread(forms.Form):
...
I would suggest you look at django's class based UpdateView. It can generate an update form for you or you could give it a custom ModelForm by overriding the form_class attribute on your view. When using a ModelForm, you also have to specify which model the form is for eg:
class EditThread(forms.ModelForm):
"field definitions ..."
class Meta:
model = Thread
fields = ['my_field_1', 'my_field_2']

django-select2 not working with inlines in django-admin

Here are my models and admin classes:
---------------------Models-----------------------
from django.contrib.auth.models import User
class PurchaseOrder(models.Model):
buyer = models.ForeignKey(User)
is_debit = models.BooleanField(default = False)
delivery_address = models.ForeignKey('useraccounts.Address')
organisation = models.ForeignKey('useraccounts.AdminOrganisations')
date_time = models.DateTimeField(auto_now_add=True)
total_discount = models.IntegerField()
tds = models.IntegerField()
mode_of_payment = models.ForeignKey(ModeOfPayment)
is_active = models.BooleanField(default = True)
class Meta:
verbose_name_plural = "Purchase Orders"
def __unicode__(self):
return '%s' % (self.id)
----------------------------------Admin----------------------------------------
"""
This class is used to add, edit or delete the details of item purchased
"""
class PurchasedItemInline(admin.StackedInline):
form = ItemSelectForm
model = PurchasedItem
fields = ['parent_category', 'sub_category', 'item', 'qty', ]
extra = 10
class BuyerChoices(AutoModelSelect2Field):
queryset = User.objects.all()
search_fields = ['username__icontains', ]
class BuyerForm(ModelForm):
user_verbose_name = 'Buyer'
buyer = BuyerChoices(
label='Buyer',
widget=AutoHeavySelect2Widget(
select2_options={
'width': '220px',
'placeholder': 'Lookup %s ...' % user_verbose_name
}
)
)
class Meta:
model = PurchaseOrder
fields = '__all__'
"""
This class is used to add, edit or delete the details of items
purchased but buyer has not confirmed the items purchased, this class
inherits the fields of PurchaseOrder derscribing the delivery address of
buyer , is_debit , total discount , tds and mode of payment
"""
class PurchaseOrderAdmin(admin.ModelAdmin):
form = BuyerForm
#list_display = ['id','buyer','delivery_address','date_time','is_active']
inlines = [PurchasedItemInline]
# model = PurchaseOrder
#actions = [mark_active, mark_inactive]
#list_filter = ['date_time']
#search_fields = ['id']
list_per_page = 20
def response_add(self, request, obj, post_url_continue=None):
request.session['old_post'] = request.POST
request.session['purchase_order_id'] = obj.id
return HttpResponseRedirect('/suspense/add_distance/')
I am trying to implement django-select2, but when I use inlines in
PurchaseOrderAdmin it doesn't show the field where I have implemented
django-select2:
But when I remove inlines, it works fine:
Edit
Here is the ItemSelectForm
class ItemSelectForm(forms.ModelForm):
class Media:
js = (
'http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
'js/ajax.js',
)
try:
parent_category = forms.ModelChoiceField(queryset=Category.objects.\
filter(parent__parent__isnull=True).filter(parent__isnull=False))
sub_category_id = Category.objects.values_list('id',flat=True)
sub_category_name = Category.objects.values_list('name',flat=True)
sub_category_choices = [('', '--------')] + [(id, name) for id, name in
itertools.izip(sub_category_id, sub_category_name)]
sub_category = forms.ChoiceField(sub_category_choices)
except:
pass
item = forms.ModelChoiceField(queryset = Product.objects.all())
def __init__(self, *args, **kwargs):
super(ItemSelectForm, self).__init__(*args, **kwargs)
self.fields['parent_category'].widget.attrs={'class': 'parent_category'}
self.fields['sub_category'].widget.attrs={'class': 'sub_category'}
self.fields['item'].widget.attrs={'class': 'item'}
It worked for me by adding the following line in the static/suit/js/suit.js
Add:
(function ($) {
Suit.after_inline.register('init_select2', function(inline_prefix, row){
$(row).find('select').select2();
});

Django and ModelForm. How to change IntegerField to dropdown box

I have a model that looks like this
class RSVP (models.Model):
def __unicode__(self):
return self.firstName + " " + self.lastName
firstName = models.CharField(max_length=30)
lastName = models.CharField(max_length=30)
rsvpID = models.CharField(max_length=9, unique = True)
allowedAdults = models.IntegerField(default = 2)
allowedChildren = models.IntegerField(default = 0)
adultsAttending = models.IntegerField(default = 0)
childrenAttending = models.IntegerField(default = 0)
and I have a ModelForm that looks like this
class RsvpForm(ModelForm):
class Meta:
model = RSVP
exclude= ('firstName', 'lastName', 'allowedAdults', 'allowedChildren')
What I would like to happen is that instead of a text field for the adultsAttending, a dropdown box with the values 0 to allowedAdults shows up. This is for a wedding rsvp site and I'd like to set the max number of +1's an invitee can bring on an individual basis
Any thoughts on how to go about this?
I'm thinking you want to fork the allowed children/ adults as well as the name to another model:
models.py
class Invited(models.Model):
f_name = models.CharField()
l_name = models.CharField()
allowed_adults = models.IntegerField()
allowed_children = models.IntegerField()
class RSVP(models.Model):
invited = models.ForeignKey(Invited)
adults_attending = models.IntegerField()
children_attending = models.IntegerField()
Then you would create the invited objects and assign the allowed adults and children. And the RSVP form would take those number into account when generating the choices for your drop down box.
The drop down can be implemented by overriding the IntegerField widget with a ChoiceField
forms.py
class InvitedForm(forms.ModelForm):
class Meta:
model = Invited
class RSVPForm(forms.ModelForm):
class Meta:
model = RSVP
exclude = ['invited',]
def __init__(self, *args, **kwargs):
max_adults = kwargs.pop('max_adults',2) #default to 2 if no max set
max_children = kwargs.pop('max_children',2) #default to 2 if no max set
super(RSVPForm, self).__init__(*args, **kwargs)
adult_choices = ( (x,str(x)) for x in range(max_adults+1)) )
children_choices = ( (x,str(x)) for x in range(max_children+1)) )
self.fields['adults_attending'] = forms.ChoiceField(choices = adult_choices)
self.fields['children_attending'] = forms.ChoiceField(choices = children_choices)
views.py
def rsvp_view(request, invited_id):
invited = get_object_or_404(Invited, pk=invited_id)
if request.method=='POST':
form = RSVPForm(request.POST, max_adults=invited.allowed_adults,
max_children=invited.allowed_children)
if form.is_valid():
rsvp = form.save(commit=False)
rsvp.invited = invited
rsvp.save()
return HttpResponse("Success")
else:
form = RSVPForm(max_adults=invited.allowed_adults, max_children=invited.allowed_children)
context = { 'form':form,
'invited':invited }
return render_to_response('rsvp.html', context,
context_instance=RequestContext(request))