Complex model:
Class Name(models.Model):
first_name = models.CharField(max_length=50,blank=True)
middle_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True)
class Person(models.Model):
names = models.ManyToManyField('Name')
wives = models.ManyToManyField('Person',related_name='husbands',null = True, blank = True)
And Form:
class PersonForm(ModelForm):
names_list = forms.ModelMultipleChoiceField(queryset=Name.objects.filter(person=2), widget=forms.CheckboxSelectMultiple(attrs={"checked":""}))
wives_list = forms.ModelMultipleChoiceField(queryset=Person.objects.filter(husbands=2), widget=forms.CheckboxSelectMultiple(attrs={"checked":""}))
class Meta:
model = Person
fields = ('names','wives')
widgets = {
'names' : forms.HiddenInput(),
'wives': forms.HiddenInput(),
}
This is hardcoded for Person number 2 (person=2, husbands=2).
And it works. But now, I want to change number 2 into variable which is related to current edited person.
If I look at html source - I can see the hidden fields:
<input id="id_names" name="names" type="hidden" value="[30, 63]" />
<input id="id_wives" name="wives" type="hidden" value="[42]" />
So - there's two ways:
Name.objects.filter(person=somevariable) - shows the names of person number
or
Name.objects.fiter(id__in=othervariable) - shows the values in names variable
(the same with wives)
But nothing works - especially - I can't find the proper variable - i'm so close....
I found the main mistake - I can't set the id in the class, because it is not set yet. It is set in __init__ constructor. So I have to change it into:
class PersonForm(ModelForm):
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
self.fields['names_list'].queryset=Name.objects.filter(person=instance.pk)
self.fields['wives_list'].queryset=Person.objects.filter(husbands=instance.pk)
names_list = forms.ModelMultipleChoiceField(queryset=Name.objects.none(), widget=forms.CheckboxSelectMultiple(attrs={"checked":""}))
wives_list = forms.ModelMultipleChoiceField(queryset=Person.objects.none(), widget=forms.CheckboxSelectMultiple(attrs={"checked":""}))
class Meta:
model = Person
fields = ('names','wives')
widgets = {
'names' : forms.HiddenInput(),
'wives': forms.HiddenInput(),
}
If someone finds a better solution - I would be appreciated.
Related
My form triggers form_invalid when the field "category" is empty.
The weird thing is, when the view displays, the "description" field does not have the asterisk indicating it's required, unlike "name" or "enabled", for instance. Also, when I try to send the form with an empty name, it correctly displays a little yellow mark and says "This field is required", but it doesn't say that when the category is empty.
So, it seems to correctly recognize that the category is not required, it just says it's invalid after I send the form.
My form looks like this:
class ProductForm(forms.Form):
name = forms.CharField(max_length=80, required=True)
category = forms.ModelChoiceField(queryset=None, required=False, label='Categoría')
description = forms.CharField(max_length=150, required=False)
price = forms.FloatField(required=True)
image = forms.ImageField(allow_empty_file=True, required=False)
extras = forms.FileField(allow_empty_file=True, required=False)
enabled = forms.BooleanField(required=False, initial=True)
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProductForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Nombre'
self.fields['description'].label = 'Descripción'
self.fields['price'].label = 'Precio'
self.fields['image'].label = 'Imagen'
self.fields['extras'].label = 'Extras'
categories = Category.objects.filter(store=Profile.objects.get(user=user).store)
if categories.count() == 0:
self.fields['category'].required = False
self.fields['category'].queryset = categories
self.fields['enabled'].label = 'Habilitado'
It is included to my view in this way:
class ProductCreateView(LoginRequiredMixin, CreateView):
template_name = 'products/product_form.html'
model = Product
fields = ["name", "category", "description", "price", "image", "enabled", "extra"]
success_url = reverse_lazy("orders:products")
And my model looks like this:
class Product(models.Model):
store = models.ForeignKey(Store, related_name="products", on_delete=models.PROTECT)
name = models.CharField(max_length=100, verbose_name="Nombre")
description = models.CharField(max_length=500, verbose_name="Descripción", null=True)
price = models.FloatField(verbose_name="Precio")
image = models.ImageField(upload_to="media/", verbose_name="Imagen", null=True, blank=True)
enabled = models.BooleanField(default=False, verbose_name="Habilitado")
extra = models.FileField(upload_to="media/files/", verbose_name="Extras", max_length=254, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT, null=True)
detail_enabled = models.BooleanField(default=False)
You never use the ModelForm you constructed. Django will create its own since nowhere you specify form_class=… [Django-doc] in your CreateView. But that will not be sufficient, since Django will not pass a user by default. You will need to override the .get_form_kwargs(…) [Django-doc] as well to pass the user.
You also should make the ProductForm a ModelForm, since otherwise it has no .save() method:
class ProductForm(forms.ModelForm):
# …
class Meta:
model = Product
fields = ['name', 'category', 'description', 'price', 'image', 'enabled', 'extra']
In your view you thus specify the form_class, and override the get_form_kwargs, to inject the user in the ModelForm constructor:
class ProductCreateView(LoginRequiredMixin, CreateView):
template_name = 'products/product_form.html'
model = Product
form_class = ProductForm
success_url = reverse_lazy('orders:products')
def get_form_kwargs(self, *args, **kwargs):
fk = super().get_form_kwargs(*args, **kwargs)
fk['user'] = self.request.user
return fk
I am trying to find out an efficient way to set a field value within form init method. My models are similar to below
class Users(models.Model):
firstname = models.CharField()
lastname = models.CharField()
class profile(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
class logindetails(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
profile = models.ForeignKey(profile, on_delete=models.PROTECT)
login_time = models.DateField(auto_now=True)
My form is like as below:
class LoginForm(forms.ModelForm):
class Meta:
model = logindetails
fields = [__all__]
def __init__(self, *args, **kwargs):
self._rowid = kwargs.pop('rowid', None)
super(LoginForm, self).__init__(*args, **kwargs)
instance = profile.objects.get(id=self._rowid)
self.fields['user'] = instance.user <--- Facing difficulties here
Any help will be appreciated.
Django had built-in ways of setting initial form values, the documentation is available here: https://docs.djangoproject.com/en/3.0/ref/forms/api/#dynamic-initial-values
I have created two models -
class DID_Definition_Model(models.Model): # DID to Region-Carrier Mapping
region_carrier = models.ForeignKey(Telco_Carrier_Mapper_Model, on_delete=models.CASCADE)
did_number= models.CharField(max_length=32, validators=[alphanumeric], primary_key=True)
did_cost= models.DecimalField(max_digits=10, decimal_places=2)
created_on_date = models.DateTimeField(default=timezone.now)
class DID_Number_Assignment_Model(models.Model): #DID Number Assignment
did_selector = models.ForeignKey(DID_Definition_Model, on_delete=models.CASCADE, primary_key=True)
subscriber_department=models.CharField(max_length=200)
usage_assignment=models.ForeignKey(Usage_Assignment_Model, on_delete=models.CASCADE)
employee_email=models.EmailField()
employee_fullname=models.CharField(max_length=200)
created_on_date = models.DateTimeField(default=timezone.now)
And created the below view -
def did_assignment_form(request, did_selector=0):
if request.method =="GET":
if did_selector==0:
form = DID_Number_Assignment_Model_Form()
else:
did_assignment_item = DID_Number_Assignment_Model.objects.get(pk=did_selector)
form = DID_Number_Assignment_Model_Form(instance=did_assignment_item)
return render(request, 'MASTERHANDLER/did_assignment_form.html', {'form':form})
else:
if id==0:
context = DID_Number_Assignment_Model.objects.values('did_selector')
if did_selector not in context:
form = DID_Number_Assignment_Model_Form(request.POST)
else:
did_assignment_item = DID_Number_Assignment_Model.objects.get(pk=did_selector)
form = DID_Number_Assignment_Model_Form(request.POST, instance = did_assignment_item)
if form.is_valid():
form.save()
return redirect('did_assignment_list')
Form detail below -
class DID_Number_Assignment_Model_Form(forms.ModelForm):
class Meta:
model = DID_Number_Assignment_Model
fields = ('did_selector', 'usage_assignment', 'employee_fullname', 'employee_email', 'subscriber_department' )
labels = {
'did_selector' : 'DID Selector',
'usage_assignment' : 'Number Usage ',
'employee_fullname' : 'Employee Full Name',
'employee_email' : 'Employee Email',
'subscriber_department' : 'Employee Department',
}
def __init__(self, *args, **kwargs):
super(DID_Number_Assignment_Model_Form,self).__init__(*args, **kwargs)
# TO SET drop down default text for a field , optional -
self.fields['did_selector'].empty_label = "Select"
self.fields['usage_assignment'].empty_label = "Select"
# TO SET a field which may be optional -
self.fields['subscriber_department'].required = False
#self.fields['region_assigned'].required = False
This form works with no problems but with one little oddity. If I create an object in DID_Number_Assignment_Model_Form with 'did_selector' field as the foreign key, the very same value of 'did_selector' is shown for the next creation process.
My question is that how can I show only those did selector values which have not been configured. Sample screenshot below -
I was able to find an answer. What essentially I was looking for a method to over ride the queryset on a select field to exclude options already used.
I did this under forms.py by first creating a queryset and getting required values for a specific field.
Then assign these values as default using 'queryset' keyword on the field.
defs_with_no_assignments = DID_Definition_Model.objects.filter(did_number_assignment_model__isnull=True)
available_did= defs_with_no_assignments.values_list('did_number', flat=True)
self.fields['did_selector'].queryset = available_did
I want to test a form. It is working, but the test doesn't.
One field of this form is popolated by a javascript function. I can use selenium to do so, but I don't want because it's giving problems and also I want isolate the test.
So I'm calling the form in my test, then I'm creating the choices (this is what javascript should do), then I'm setting the fields values.
My models.py:
class Name(models.Model):
name = models.CharField(_('nome'), max_length=50, default='')
namelanguage = models.ForeignKey(
NameLanguage, related_name='%(app_label)s_%(class)s_language',
verbose_name=_('linguaggio'), on_delete=models.PROTECT)
nametype = models.ForeignKey(
NameType, related_name='%(app_label)s_%(class)s_tipo',
verbose_name=_('tipo'), on_delete=models.PROTECT)
gender = models.ForeignKey(
Gender, related_name='%(app_label)s_%(class)s_gender',
verbose_name=_('sesso'), on_delete=models.PROTECT,
blank=True, null=True)
usato = models.PositiveSmallIntegerField(_('usato'), default=0)
approved = models.BooleanField(null=True, blank=True, default=False)
def save(self, *args, **kwargs):
self.name = format_for_save_name(self.name)
to_save = check_gender_name(self)
if not to_save:
return
else:
super(Name, self).save(*args, **kwargs)
def format_for_save_name(name):
myname = name.lower().strip()
if myname[0] not in "abcdefghijklmnopqrstuvwxyz#":
myname = '#' + myname
return myname
My form.py:
class NameForm(forms.ModelForm):
class Meta:
model = Name
fields = ['namelanguage', 'nametype', 'gender', 'name', 'usato',
'approved']
widgets = {
'gender': forms.RadioSelect(),
'usato': forms.HiddenInput(),
'approved': forms.HiddenInput(),
}
My test_form.py:
def test_form_validation(self):
maschio = Gender.objects.create(name_en='Male', name_it='Maschio')
nome = NameType.objects.create(name_en='Name', name_it='Nome')
romani = NameLanguage.objects.create(
name_en='Romans', name_it='Romani')
romani.sintassi.add(nome)
form = NameForm()
form.fields['nametype'].disabled = False
form.fields['nametype'].choices = [(nome.id, nome)]
form.fields['nametype'].initial = nome.id
form.fields['gender'].initial = maschio.id
form.fields['name'].initial = 'Bill'
form.fields['namelanguage'].initial = romani.id
# form.fields['usato'].initial = 0
# form.fields['approved'].initial = False
print('1', form)
# self.assertTrue(form.is_valid())
form.save()
print('1', form) gives a form without errors but form.is_valid is False and (when is commented out) form.save() gives an error when the model try to save the name field:
if myname[0] not in "abcdefghijklmnopqrstuvwxyz#":
IndexError: string index out of range
That is because the name is an empty string and yet my print('1', form) gives all the fields with the right options selected and specifically the name field isn't empty but has value="Bill":
<td><input type="text" name="name" value="Bill" maxlength="50" autofocus="" required id="id_name">
Edit. I tried to avoid that check and the problem is the same for the other fields: they looks ok on the print('1', form) but they don't arrive to the form.save(), for example in my print('1', form) I have:
<tr><th><label for="id_namelanguage">Linguaggio:</label></th><td><select name="namelanguage" required id="id_namelanguage">
<option value="">---------</option>
<option value="1" selected>Romani</option>
so it looks I have selected an option but then I receive this error:
django.db.utils.IntegrityError: NOT NULL constraint failed: lists_name.namelanguage_id
I don't know why and how but this code is working:
def test_form_validation(self):
maschio = Gender.objects.create(name_en='Male', name_it='Maschio')
nome = NameType.objects.create(name_en='Name', name_it='Nome')
romani = NameLanguage.objects.create(
name_en='Romans', name_it='Romani')
romani.syntax.add(nome)
form = NameForm({'nametype': nome.id, 'gender': maschio.id,
'name': 'Remo', 'namelanguage': romani.id})
form.fields['nametype'].initial = nome.id
form.save()
self.assertEqual(Name.objects.all().count(), 1)
my_name = Name.objects.first()
self.assertEqual(my_name.name, 'remo')
self.assertEqual(my_name.nametype, nome)
self.assertEqual(my_name.gender, maschio)
self.assertEqual(my_name.namelanguage, romani)
Any comment will be appreciated
My goal is to loop through all form fields and to assign certain classes to them like this:
class ContactForm(forms.Form):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control input-sm plain'
if field.required == True:
field.widget.attrs['required'] = ''
class Meta:
model = Contact
fields = '__all__'
The issue with this code is that self.fields.items() seems to be empty (and as a result I never get into the for-loop).
My guess is that the issue arose either because of my upgrade from Django 1.9 and python 2 to Django 1.10 and python 3, or because of custom manager present in the definition of the underlying model.
Could anyone share expertise on this?
class Contact(BaseMixin, DeleteMixin):
provider_account = models.ForeignKey(ProviderAccount, models.DO_NOTHING)
client_id = models.IntegerField()
name = models.CharField(max_length=300)
profile_photo_url = models.CharField(max_length=100, default = 'no_image.jpg')
event_type_id = models.IntegerField(EventType.choices(), blank=True, null=True)
is_satisfied = models.NullBooleanField()
objects = CustomQuerySetManager()
class Meta:
managed = False
db_table = 'contact'
class QuerySet(QuerySet):
#....
Your form is a standard form, not a model form; the Meta class is ignored and the only fields are those you define yourself.
Your form should inherit from forms.ModelForm for this to work.