Django: field validator and UNIQUE constraint are not applyed - django

I have a Randomisation model with pat field that should be unique and have a specified format.
I defined unique=true and and validators but neither are applyed
For example, FRA-0001 sould be valid and if I try to registered this code twice it should raise an error validation message on my form (but not crash database).
But currently, I can registered FRA for example and if I registered it twice I raise database error UNIQUE contraint failed
models.py
class Randomisation(models.Model):
...
pat = models.CharField("Patient number", max_length=8, unique=True, null=True, blank=True,
validators = [
RegexValidator(
regex='^[A-Z]{3}-[0-9]{4}$',
message= 'L\'identifiant doit être au format XXX-0000',
code='invalid_participant_id'
),
],
)
...
forms.py
class RandomizationEditForm(forms.Form):
def __init__(self, request, *args, **kwargs):
super(RandomizationEditForm, self).__init__(*args, **kwargs)
self.user_country = Pays.objects.get(pay_ide = request.session.get('user_country'))
# self.language = request.session.get('language')
# print(self.language)
self.user_site_type = request.session.get('user_site_type')
PAYS = Pays.options_list(self.user_country,self.user_site_type,'fr')
SEXE = Thesaurus.options_list(2,'fr')
STRATE_1 = Thesaurus.options_list(3,'fr')
STRATE_2 = Thesaurus.options_list(4,'fr')
YES = [(None,''),(0,'Non'),(1,'Oui'),]
self.fields["pat"] = forms.CharField(label = "Numéro patient (XXX-0000)")
self.fields['pat'].widget.attrs.update({
'autocomplete': 'off'
})
self.fields["ran_nai"] = forms.IntegerField(label = "Date de naissance (année)", widget=forms.TextInput)
self.fields['ran_nai'].widget.attrs.update({
'autocomplete': 'off'
})
self.fields["ran_sex"] = forms.ChoiceField(label = "Sexe", widget=forms.Select, choices=SEXE)
self.fields["ran_st1"] = forms.ChoiceField(label = "Gravité de la maladie COVID-19", widget=forms.Select, choices=STRATE_1)
self.fields["ran_bug"] = forms.BooleanField(label = "Recours à la procédure de secours ?", required = False)
self.fields["ran_dem_nom"] = forms.CharField(label = "Nom de la personne qui demande la randomisation", required = False, initial=None)
self.fields['ran_dem_nom'].widget.attrs.update({
'autocomplete': 'off'
})
self.fields["ran_dem_dat"] = forms.DateField(
# input_formats=settings.DATE_INPUT_FORMATS,
label = "Date de la demande",
initial = timezone.now(),
required = False,
)
self.fields['ran_dem_dat'].widget.attrs.update({
'autocomplete': 'off'
})
# def clean_pat(self):
# data = self.cleaned_data['pat']
# if len(data) < 8:
# raise forms.ValidationError("Merci de contrôler la numéro patient")
# return data
def clean_ran_nai(self):
data = self.cleaned_data['ran_nai']
if data > 2003:
raise forms.ValidationError("Merci de contrôler la date de naissance")
return data
def clean_ran_dem_dat(self):
data = self.cleaned_data['ran_dem_dat']
entrydate = datetime.datetime.strptime(str(data), "%Y-%m-%d")
currentdate = datetime.datetime.now()
if entrydate > currentdate:
raise forms.ValidationError("Merci de contrôler la date (postérieure à la date du jour)")
return data
views.py
def randomization_edit(request):
if request.method == "POST":
form = RandomizationEditForm(request, data=request.POST or None)
if form.is_valid():
# Récupération des données permettant la randomisation
randomisation = Randomisation.objects.filter(Q(pay_ide=form.data.get('pay_ide')) & Q(ran_act=1) & Q(ran_st1=form.data.get('ran_st1')) & Q(pat=None)).first()
randomisation.pat = form.cleaned_data['pat']
randomisation.ran_nai = form.data.get('ran_nai')
randomisation.ran_sex = form.data.get('ran_sex')
randomisation.ran_bug = form.cleaned_data['ran_bug']
if form.data.get('ran_dem_nom') == '':
randomisation.ran_dem_nom = None
else:
randomisation.ran_dem_dat = form.data.get('ran_dem_dat')
randomisation.ran_log = request.user.username
randomisation.ran_dat = timezone.now()
randomisation.save()
return redirect('randomization:confirmation', pk = randomisation.pk)
else:
form = RandomizationEditForm(request)
return render(request, 'randomization/edit.html', {'form': form})

You're redefining the pat field as a CharField in your form, so none of the validations you specified on the model apply. You can either rewrite your form to be a ModelForm or add the regex and uniqueness validation into your form.
class RandomizationEditForm(forms.Form):
def __init__(self, *args, **kwargs):
super(RandomizationEditForm, self).__init__(*args, **kwargs)
self.fields["pat"] = forms.CharField(label = "Numéro patient (XXX-0000)", validators=[RegexValidator(
regex='^[A-Z]{3}-[0-9]{4}$',
message= 'L\'identifiant doit être au format XXX-0000',
code='invalid_participant_id'
)])
def clean_pat(self):
data = self.cleaned_data['pat']
if Randomisation.objects.filter(pat=data).exists():
raise forms.ValidationError("L\'identifiant doit être unique")
return data

Related

Django - form_valid() takes 2 positional arguments but 5 were given - Multiple forms

I am new using Django and I want to save 4 different forms using CreateView. I am able to save 3 of them:
Product Form
Product Cost Form
Product Prices Form
But with Product inventory Form, I am getting this error: form_valid() takes 2 positional arguments but 5 were given.
Product inventory Form:
class ProdInvForm2(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
Column('invmax', css_class='form-group col-md-6 mb-0'),
Column('invmin', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
)
class Meta:
model = ProdInv
# fields = ('invmin', 'invmax')
widgets = {
'invmax': NumberInput(
attrs={
'placeholder': 'Inventario Maximo',
}
),
'invmin': NumberInput(
attrs={
'placeholder': 'Inventario Minimo',
}
),
}
# esto es para excluir campos que no quieres que aparezcan
#, 'invact', 'invord', 'invres'
exclude = ['user_updated', 'user_creation', 'prod', 'invact', 'invord', 'invres']
Product Inventory Models:
class ProdInv(BaseModel):
prod = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='Producto')
invact = models.IntegerField(default=0, null=True, blank=True, verbose_name='Inventario Actual')
invres = models.IntegerField(default=0, null=True, blank=True, verbose_name='Inventario Reservado')
invord = models.IntegerField(default=0, null=True, blank=True, verbose_name='Inventario Ordenado')
invmax = models.IntegerField(default=0, blank=True, verbose_name='Inventario Maximo')
invmin = models.IntegerField(default=0, blank=True, verbose_name='Inventario Minimo')
def toJSON(self):
item = model_to_dict(self, exclude=['user_creation', 'user_updated'])
item['prod'] = self.prod.toJSON()
return item
def __str__(self):
return self.prod.name
class Meta:
verbose_name = 'Inventario de Producto'
verbose_name_plural = 'Inventario de Productos'
ordering = ['id']
CreateView:
class ProductCreateView(LoginRequiredMixin, ValidatePermissionRequiredMixin, CreateView):
model = Product
form_class = ProductForm
template_name = 'product/create.html'
success_url = reverse_lazy('erpInv:product_list')
permission_required = 'erpAdm.add_product'
url_redirect = success_url
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
data = {}
try:
action = request.POST['action']
if action == 'add':
AttrValueFormSet = formset_factory(AttrValueForm, can_delete=True, extra=1)
formprod = ProductForm(request.POST)
formcost = ProdCostForm(request.POST)
formprice = ProdPriceForm(request.POST)
formprodinv = ProdInvForm2(request.POST)
AV_formset = AttrValueFormSet(request.POST)
with transaction.atomic():
if formprod.is_valid() and formcost.is_valid() and formprice.is_valid() \
and formprodinv.is_valid():
return self.form_valid(formprod, formcost, formprice, formprodinv)
else:
return self.form_invalid(formprod, formcost, formprice, formprodinv)
else:
data['error'] = 'No ha ingresado a ninguna opción'
except Exception as e:
data['error'] = str(e)
# acuerdate cuando es coleccion de elementos, hay que serializar con safe=flase
return JsonResponse(data, safe=False)
def form_valid(self, formprod, formcost, formprice, formprodinv):
# hubo que cmentar e save del form, para que funcione el save()
with transaction.atomic():
prod = formprod.save(commit=True)
print(prod.pk)
price = formprice.save(commit=False)
price.prod_id = prod.pk
cost = formcost.save(commit=False)
cost.prod_id = prod.pk
print(cost)
inv = formprodinv.save(commit=False)
inv.prod_id = prod.pk
print(inv)
inv.save()
cost.save()
price.save()
return super().form_valid(formprod, formcost, formprice, formprodinv)
def form_invalid(self, formprod, formcost, formprice, formprodinv):
return self.render_to_response(
self.get_context_data(formprod=formprod.errors, formcost=formcost.errors, formprice=formprice.errors,
formprodinv=formprodinv.errors))
# Dont Know which one to use as return statement
# return super().form_invalid(formprod=formprod.errors, formcost=formcost.errors, formprice=formprice.errors,
# formprodinv=formprodinv.errors)
def get_context_data(self, **kwargs):
# AVForm = AttrValueForm(prefix='avprefix')
AttrValueFormSet = formset_factory(AttrValueForm, can_delete=True, extra=1)
context = super().get_context_data(**kwargs)
context['title'] = 'Creación un Producto'
context['entity'] = 'Productos'
context['list_url'] = self.success_url
context['action'] = 'add'
context['formcost'] = ProdCostForm()
context['formprice'] = ProdPriceForm(prefix='precio')
context['formprodinv'] = ProdInvForm2(prefix='invent')
# Next step try to use formset to save product attributes
# context['AV_formset'] = AttrValueFormSet(prefix='avprefix')
# context['avHelper'] = AVHelper()
return context
I did the test and comment "transaction.atomic()". I was able to save to other forms, but do not know why I am having this problem with the Product Inventory Form.
Hope someone can highlight what I am doing wrong.
Cheers and Happy New Year
To solve my problem I saved the forms inside de def POST. Instead of using the form_valid().
def post(self, request, *args, **kwargs):
data = {}
try:
action = request.POST['action']
if action == 'add':
AttrValueFormSet = formset_factory(AttrValueForm, can_delete=True, extra=1)
formprod = ProductForm(request.POST)
formcost = ProdCostForm(request.POST)
formprice = ProdPriceForm(request.POST)
formprodinv = ProdInvForm2(request.POST)
AV_formset = AttrValueFormSet(request.POST)
with transaction.atomic():
if formprod.is_valid() and formcost.is_valid() and formprice.is_valid() and formprodinv.is_valid():
print('los form son validos')
prod = formprod.save(commit=True)
print(prod.pk)
price = formprice.save(commit=False)
print('precio')
print(formprice.cleaned_data)
price.prod_id = prod.pk
cost = formcost.save(commit=False)
print('costo')
cost.prod_id = prod.pk
inv = formprodinv.save(commit=False)
print('Inventario')
print(formprodinv.cleaned_data)
inv.prod_id = prod.pk
inv.save()
cost.save()
price.save()
else:
print('los forms NO son validos')
return formprod.errors, formprice.errors, formcost.errors, formprodinv.errors
else:
data['error'] = 'No ha ingresado a ninguna opción'
except Exception as e:
data['error'] = str(e)
# acuerdate cuandp es coleccion de elementos, hay que serializar con safe=flase
return JsonResponse(data, safe=False)

Django: 'UNIQUE constraint failed'

I have override init in RandomisationFOrm and have an error I did not have before
I understand the error: in my models Randomization ran_num field is UNIQUE
and when I try to save a form when I call randomisation_edit view with the same ran_num it raise error
but before overriding __init__, this error was manage by Django itself
I try to add validation as below but raise another error: 'RandomisationForm' object has no attribute 'clean_data' -> I do the same for other field without this error...
def clean_ran_num(self):
data = self.clean_data['ran_num']
if data == 'CIV-CO-001':
raise forms.validationError('test')
return data
I try to add validation in the global 'clean' method but it do not apply...
I do not understand...
models.py
class Randomisation(models.Model):
# ran_num = models.CharField("Patient code", max_length=10, unique=True, null=True, blank=True,validators=[validate_patient_code])
ran_ide = models.AutoField(primary_key=True)
ran_num = models.CharField("Patient code", max_length=10, unique=True, null=True, blank=True)
ran_dat = models.DateField("Date of randomization", null=True, blank=True)
ran_inv = models.CharField("Investigator", max_length=20, null=True, blank=True)
ran_pro = models.IntegerField("Procedure used to randomized", null=True, blank=True)
ran_pro_per = models.CharField("if telephone, name of the person reached on the phone", max_length=20, null=True, blank=True)
ran_crf_inc = models.IntegerField("Was the CRF inclusion 2 Eligibility fulfilled?", null=True, blank=True)
ran_tbc = models.IntegerField("Is TB diagnosis possible, probable or definite?", null=True, blank=True)
ran_crf_eli = models.IntegerField("Was the CRF Inclusion 2 Eligibility fulffiled?", null=True, blank=True)
ran_cri = models.IntegerField("Is the conclusion All criteria fulfilled for randomisation", null=True, blank=True)
ran_sta = models.IntegerField("British Medical Council Staging (worst stage reported between first symptoms and now)", null=True, blank=True)
ran_vih = models.IntegerField("HIV/AIDS status", null=True, blank=True)
ran_bra = models.IntegerField("TB treatment assigned", null=True, blank=True)
ran_med = models.CharField("Batch number", max_length=6, null=True, blank=True)
ran_log_dat = models.DateTimeField("Date", null=True, blank=True)
ran_log = models.CharField("Name of the person who performs randomisation on site", max_length=10, null=True, blank=True)
forms.py
class RandomisationForm(forms.ModelForm):
# surcharge méthode constructeur (__init__) pour avoir accès aux variables de sessions
# https://stackoverflow.com/questions/3778148/django-form-validation-including-the-use-of-session-data
def __init__(self, request, *args, **kwargs):
self.request = request
super(RandomisationForm, self).__init__(*args, **kwargs)
# vérification que le numéro patient saisi correspond bien au site actuellement sélectionné
# i.e. profil 'International'
def clean(self):
data = self.cleaned_data['ran_num']
if data[4:6] != self.request.session.get('selected_site'):
raise ValidationError(_('Patient code is different from the selected site'))
TYPES = [
(None, ''),
(1, _('On-line')),
(2, _('Telephon')),
]
YESNO = [
(None, ''),
(1, _('Yes')),
(0, _('No')),
]
TB = [
(None, ''),
(1, _('Possible')),
(2, _('Probable')),
(3, _('Definite')),
]
SEVERITY = [
(None, ''),
(1, _('Mild')),
(2, _('Severe')),
]
HIV = [
(None, ''),
(0, _('Negative')),
(1, _('Positive')),
]
ran_dat = forms.DateField(
label = _("Date of randomization"),
initial = datetime.datetime.now,
required = True,
)
ran_num = forms.CharField(label = _("Patient number"), required=True) # ajout
ran_inv = forms.CharField(label = _("Investigator"), required=True)
ran_pro = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)
ran_pro_per = forms.CharField(label = _("if telephone, name of the person reached on the phone"), required=False)
ran_crf_inc = forms.ChoiceField(label = _("Was the CRF inclusion 1 TB diagnosis fulfilled?"), widget = forms.Select, choices = YESNO)
ran_crf_eli = forms.ChoiceField(label = _("Was the CRF Inclusion 2 Eligibility fulffiled?"), widget = forms.Select, choices = YESNO)
ran_tbc = forms.ChoiceField(label = _("Is TB diagnosis possible, probable or definite?"), widget = forms.Select, choices = TB)
ran_cri = forms.ChoiceField(label = _("Is the conclusion All criteria fulfilled for randomisation"), widget=forms.Select, choices = YESNO)
ran_sta = forms.ChoiceField(label = _("British Medical Council Staging (worst stage reported between first symptoms and now)"), widget = forms.Select, choices = SEVERITY)
ran_vih = forms.ChoiceField(label = _("HIV/AIDS status"), widget = forms.Select, choices = HIV)
class Meta:
model = Randomisation
# Tous les champs sauf les champs de log et les champs TB treatment et Drug batch number
fields = ('ran_num','ran_dat','ran_inv','ran_pro','ran_pro_per','ran_crf_inc','ran_tbc','ran_crf_eli','ran_cri','ran_sta','ran_vih',)
def clean_ran_crf_inc(self):
data = self.cleaned_data['ran_crf_inc']
if int(data) == 0:
raise forms.ValidationError(_("This criteria is mandatory for randomization"))
return data
def clean_ran_crf_eli(self):
data = self.cleaned_data['ran_crf_eli']
if int(data) == 0:
raise forms.ValidationError(_("This criteria is mandatory for randomization"))
return data
def clean_ran_cri(self):
data = self.cleaned_data['ran_cri']
if int(data) == 0:
raise forms.ValidationError_(("This criteria is mandatory for randomization"))
return data
views.py
def randomisation_edit(request):
if request.method == "POST":
form = RandomisationForm(request, data=request.POST or None)
if form.is_valid():
randomisation = form.save()
# randomisation
rand = Randomiser(randomisation.ran_num, randomisation.ran_vih, randomisation.ran_sta)
# Mise à jour de la base de données -> Patient code pour rendre indisponible la ligne
# pour une prochaine randomisation
# ListeRandomisation
# 1.Récupération de la ligne dans la liste de rando
bras = rand['bras']
# 2.Mise à jour de la ligne dans la liste de rando
patient = ListeRandomisation.objects.get(ran_ide = bras)
patient.ran_dis = randomisation.ran_num # pat
patient.save()
# Medicament
# 1.Récupération de la ligne dans la liste de correspondance médicament
medicament = rand['medicament']
# 2.Mise à jour de la ligne dans la liste de correspondance médicament
medicament = Medicament.objects.get(med_ide = medicament)
medicament.ran_dis = randomisation.ran_num # pat
medicament.save()
# mise à jour de la variable de session can_randomize
request.session['can_randomize'] = is_randomizable(medicament.ran_dis)
# Mise à jour de la table Randomisation avec le bras et le numéro de boite attribués
randomisation.ran_bra = patient.ran_bra
randomisation.ran_med = medicament.med_num
randomisation.ran_log_dat = datetime.now()
randomisation.ran_log = request.user.username
randomisation.save()
return redirect('randomization:confirmation', pk = randomisation.pk)
else:
form = RandomisationForm(request)
return render(request, 'randomization/randomisation_edit.html', {'form': form})
When you override the Form.clean method you have to call super(RandomisationForm, self).clean() or you will loose the unique constraint checks.
From the docs:
https://docs.djangoproject.com/en/3.0/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
The call to super().clean() in the example code ensures that any validation logic in parent classes is maintained. If your form inherits another that doesn’t return a cleaned_data dictionary in its clean() method (doing so is optional), then don’t assign cleaned_data to the result of the super() call and use self.cleaned_data instead:

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']

Editing Fromset ModelChoiceField

When i edit formset the ModelChoiceField don't show initial value (it show the empty label). I use this example
https://whoisnicoleharris.com/2015/01/06/implementing-django-formsets.html
demande\forms.py
class LinkForm(forms.Form):
etabz = forms.ModelChoiceField(empty_label="Choisir un établissement",\
queryset=Etablissement.objects.all().order_by('univ','etablissement').distinct(),
)
# etabz = forms.CharField()
def __init__(self, *args, **kwargs):
dbz = kwargs.pop("dbz", None)
super(LinkForm, self).__init__(*args, **kwargs)
if dbz:
self.fields["etabz"].queryset = Etablissement.objects.filter(univ__corps=dbz)
class BaseLinkFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
return
anchors = []
urls = []
duplicates = False
for form in self.forms:
if form.cleaned_data:
dona = form.cleaned_data.get('etabz')
anchor = dona.etablissement
if anchor :
if anchor in anchors:
duplicates = True
anchors.append(anchor)
if duplicates:
raise forms.ValidationError(
'Vous ne pouvez choisir le même établissement plusiuers fois.',
code='duplicate_etab'
)
def __init__(self, *args, **kwargs):
super(BaseLinkFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
Demande\views.py
#login_required
def demande(request):
user = request.user
corpsorigine = user.profile.corps
uz = get_object_or_404(session, active=True, destine = user.profile.corps)
choice_links = choix.objects.filter(demandeur = user, session = uz )
link_data = [{'etabz': l}
for l in choice_links]
LinkFormSet = formset_factory(LinkForm, formset=BaseLinkFormSet, extra=1)
if request.method == 'POST':
link_formset = LinkFormSet(request.POST,form_kwargs={'dbz':corpsorigine })
if link_formset.is_valid():
new_links = []
for link_form in link_formset:
dona = link_form.cleaned_data.get('etabz')
try:
univ = dona.univ
etablissementa = dona.etablissement
except:
messages.error(request, 'Une exception à provoqué une erreur. veuillez ré-essayer ultérieurement')
return redirect(reverse('blog:post_list'))
if etablissementa and univ:
new_links.append(choix(demandeur = user,session=uz.annee_session, etablissement_demande=etablissementa, universite_demande=univ))
try:
with transaction.atomic():
UserLink = choix.objects.all()
UserLink.filter(session= uz.annee_session,demandeur = user).delete()
UserLink.bulk_create(new_links)
messages.success(request, 'Votre demande à été correctement saisie/mis à jour.')
return redirect(reverse('blog:post_list'))
except IntegrityError: # If the transaction failed
messages.error(request, 'Une exception à provoqué une erreur. veuillez ré-essayer ultérieurement')
return redirect(reverse('blog:post_list'))
else:
link_formset = LinkFormSet(initial=link_data,form_kwargs={'dbz':corpsorigine })
context = {
'uz':uz,
'link_formset': link_formset,
'choice_links':choice_links
}
return render(request, 'edit_pro.html', context)
Account\Models.py
class universite (models.Model):
gouvernorat= models.CharField(max_length=250)
universite = models.CharField(max_length=250)
corps = models.CharField(max_length=20, choices=corps_CHOICES)
class Meta:
unique_together = ('gouvernorat', 'universite')
def __str__(self):
return self.universite
class etablissement(models.Model):
etablissement = models.CharField(max_length=250)
univ = models.ForeignKey(universite, related_name='etab_universites')
def __unicode__(self):
return "{} de l'universite {}".format(self.etablissement,self.univ)
Demande\models.py
class choix(models.Model):
demandeur = models.ForeignKey(User, related_name='user_choice')
# universite_demande = models.ForeignKey(universite, related_name='universite_choice')
session = models.CharField(max_length=250)
universite_demande = models.CharField(max_length=250)
etablissement_demande = models.CharField(max_length=250)
class Meta:
unique_together = ('demandeur', 'session','etablissement_demande')
def __unicode__(self):
return "{} de l'universite {}".format(self.etablissement_demande, self.universite_demande)
In demande\forms.py If i change forms.ModelChoiceField... to forms.CharField it works perfectly. How it can works on ModelChoiceField ?

Django: saving inlineformset_factory throws (1048, "Column 'XX_id' cannot be null")

I am trying to save an inlineformset_factory form, if i am editing one object that already exists there is no problem. If it is the first time that i create the object i have a 1048, "Column 'parte_id' cannot be null") error.
my models.py:
class Parte(models.Model):
cliente = models.ForeignKey(Cliente)
averia = models.CharField(max_length=100)
finalizada = models.BooleanField('Finalizado', default=False)
firmada = models.BooleanField('Autorizado', default=False)
class Material(models.Model):
parte = models.ForeignKey(Parte)
nombre = models.CharField(max_length=20)
cantidad = models.IntegerField(max_length=3)
class Prestacion(models.Model):
parte = models.ForeignKey(Parte)
equipo = models.CharField(max_length=100, blank=True)
tiempo = models.DecimalField(max_digits=3, decimal_places=1)
class Observacion(models.Model):
parte = models.OneToOneField(Parte)
notas = models.TextField(max_length=750,null=True, blank=True)
my forms.py:
MaterialFormSet = inlineformset_factory(Parte,Material,extra=3)
PrestacionFormSet = inlineformset_factory(Parte,Prestacion,extra=1)
class ObservacionForm(forms.ModelForm):
class Meta:
model = Observacion
exclude = ('parte',)
class ParteForm(forms.ModelForm):
class Meta:
model = Parte
and my views.py:
#login_required()
def editar_parte(request, parte_id=None):
parte = None
observacion = None
# Comprobamos si el parte se puede guardar o no
permitido = True
if parte_id:
parte = Parte.objects.get(pk=parte_id) # probar get_or_404
try:
observacion = Observacion.objects.get(parte=parte_id)
except:
pass # observacion = None
if parte.procesado or parte.enviado:
permitido = False
formParte = ParteForm(instance=parte)
formPrestacion = PrestacionFormSet(instance=parte)
formMaterial = MaterialFormSet(instance=parte)
formObservacion = ObservacionForm(instance=observacion)
if request.method == 'POST':
formParte = ParteForm(request.POST, instance=parte)
formPrestacion = PrestacionFormSet(request.POST, instance=parte)
formMaterial = MaterialFormSet(request.POST, instance=parte)
formObservacion = ObservacionForm(request.POST, instance=observacion)
if formParte.is_valid() and formPrestacion.is_valid() and formMaterial.is_valid() and formObservacion.is_valid():
# Preguardamos el parte (nos genera una pk)
parte = formParte.save(commit=False)
parte.save()
formPrestacion.save() #HERE IS THE PROBLEM
formMaterial.save()
if observacion:
observacion.parte_id = parte.pk
observacion.save()
if request.POST.get('enviar')=='True':
success ="enviado" # Enviamos el parte
return redirect('gestion.views.listados.list_partes')
else:
error.append("Corregir los siguientes errores:")
context = {'formParte': formParte, 'formPrestacion': formPrestacion, 'formMaterial': formMaterial, 'formObservacion': formObservacion,}
return render(request, 'partes/editar.html', context)
it seems that when i save the inlineformset_factory the objects have their own ForeignKey empty.
i have tried to change formPrestacion.save() for:
if formPrestacion.has_changed():
for data in formPrestacion:
prest = data.save(commit=False)
prest.parte_id = parte.pk
prest.save()
but i have problems when i delete the objects and i don't think that it is the solution.
I think that i found the mistake. I have changed this:
if request.method == 'POST':
formParte = ParteForm(request.POST, instance=parte)
formPrestacion = PrestacionFormSet(request.POST, instance=parte)
formMaterial = MaterialFormSet(request.POST, instance=parte)
formObservacion = ObservacionForm(request.POST, instance=observacion)
if formParte.is_valid() and formPrestacion.is_valid() and formMaterial.is_valid() and formObservacion.is_valid():
by this:
if request.method == 'POST':
formParte = ParteForm(request.POST, instance=parte)
formObservacion = ObservacionForm(request.POST, instance=observacion)
if formParte.is_valid():
parte = formParte.save(commit=False)
formPrestacion = PrestacionFormSet(request.POST, instance=parte)
formMaterial = MaterialFormSet(request.POST, instance=parte)
if formPrestacion.is_valid() and formMaterial.is_valid() and formObservacion.is_valid():