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
Related
I'm trying to create an appointment app using Django
but when I add the form it show me all the users
how can i change that to only one type of user
and make the user who register the appointment him self
this is my models.py
class User(AbstractUser):
STATUS_CHOICES = (('paitent', 'paitent'), ('Doctor', 'Doctor'), ('reception', 'reception'), ('temporary', 'temporary'))
STATUS_CHOICES_2 = (('yes', 'yes'), ('no', 'no'))
type_of_user = models.CharField(max_length=200, choices=STATUS_CHOICES, default='paitent')
allowd_to_take_appointement = models.CharField(max_length=20, choices=STATUS_CHOICES_2, default='yes')
def is_doctor(self):
if self.type_of_user == 'Doctor':
return True
else:
return False
def is_paitent(self):
if self.type_of_user == 'paitent':
return True
else:
return False
class Appointement_P(models.Model):
user_ho_add = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_ho_add_appointement')
patient = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name='paitent_app')
doctor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='doctor_app')
date = models.Field(null=True, blank=True, default=timezone.now)
start_time = models.TimeField(null=True, blank=True, default=timezone.now)
end_time = models.TimeField(null=True, blank=True, default=timezone.now)
and this is my fomrs.py
class AppointementForm(forms.ModelForm):
class Meta:
model = Appointement_P
fields = ('doctor', 'date', 'start_time',)
and this is my fucntion in the views.py
def create_appointement_p(request):
user = User()
form_appointement = AppointementForm()
if request.method=='POST':
if request.user.is_paitent():
form_appointement = AppointementForm(request.POST or None)
if form_appointement.is_valid():
form_app = form_appointement.save(commit=False)
form_app.save()
messages.success(request, 'appointement added')
else:
messages.error(request, 'Error')
return render(request,'appointement/add_appointement1.html',)
else:
return HttpResponseRedirect(reverse("create_appointement_D"))
return render(request,'appointement/add_appointement1.html',{'form':form_appointement})
and this is the html file
<body>
<div class="container">
{{ form }}
{% csrf_token %}
<button>
ok
</button>
</form>
</div>
</body>
the problem is in the doctoral field, it show me all the user how can I change that to only users that have a type of doctors
how can I make the user_ho_add automatically the one ho register this appointment?
how can I make the end time of the appointment the start_time + 30 min?
For filtering the doctor field you can customize it in you ModelForm class, like this:
# import your User model first
class AppointementForm(forms.ModelForm):
doctor = forms.ModelChoiceField(queryset=User.objects.filter(type_of_user='Doctor'))
class Meta:
model = Appointement_P
fields = ('doctor', 'date', 'start_time',)
Then for filling the user_ho_add field, just add the user before saving the form_app instance, like this:
def create_appointement_p(request):
...
if form_appointement.is_valid():
form_app = form_appointement.save(commit=False)
form_app.user = request.user # user added here
form_app.save()
messages.success(request, 'appointement added')
...
Bonus tip: for the is_paitent and is_doctor methods, you can simply return the comparison, since the result is already a boolean, like this:
def is_doctor(self):
return self.type_of_user == 'Doctor'
def is_paitent(self):
return self.type_of_user == 'paitent'
UPDATE
As requested in the comment, for adding end_time = start_time + 30min you first need to import the timedelta class from the datetime library.
from datetime import timedelta
# your other imports
...
def create_appointement_p(request):
...
if form_appointement.is_valid():
form_app = form_appointement.save(commit=False)
form_app.user = request.user # user added here
form_app.end_time = form_app.start_time + timedelta(minutes=30) # end_time added here
form_app.save()
messages.success(request, 'appointement added')
...
Bonus tip 2: If you're using Django 3.1 (or above) you can use the models.TextChoices class to create a cleaner way to reference your choices, like this:
class TypeOfUser(models.TextChoices):
# REFERENCE_VALUE = 'string to save in the field', 'string to display (supports translation)'
PAITENT = 'paitent', 'Patient'
DOCTOR = 'doctor', 'Doctor'
RECEPTION = 'reception', 'Reception'
TEMPORARY = 'temporary', 'Temporary'
class AllowdToTakeAppointement(models.TextChoices):
YES = 'yes', 'Yes'
NO = 'no', 'No'
class User(AbstractUser):
type_of_user = models.CharField(max_length=200, choices=TypeOfUser, default=TypeOfUser.PAITENT)
allowd_to_take_appointement = models.CharField(
max_length=20, choices=AllowdToTakeAppointement.choices, default=AllowdToTakeAppointement.YES
)
def is_doctor(self):
return self.type_of_user == TypeOfUser.DOCTOR
def is_paitent(self):
return self.type_of_user == TypeOfUser.PAITENT
Then you can import the TypeOfUser class for your AppointementForm class:
# import your User model and TypeOfUser
class AppointementForm(forms.ModelForm):
doctor = forms.ModelChoiceField(queryset=User.objects.filter(type_of_user=TypeOfUser.DOCTOR))
class Meta:
model = Appointement_P
fields = ('doctor', 'date', 'start_time',)
Hej,
I have multiple options created by an SQL Select, one Example looks like this:
<select name="FirmaID" class="selectbox" id="id_FirmaID">
<option value="1" selected="">Westcoast Computer</option>
<option value="2">AppleNet</option>
<option value="9">amy.rau.codes</option>
<option value="11">Cybershield IVS</option>
</select>
You can see value is correct the ID of this Company, the problem is if I click on save its not saving. But if I change my queryset that the output looks like:
<option value="1" selected="">1</option>
then it's saving fine. But this looks not user-friendly. Why is Django try to save the text behind the value and not the value?
My view looks like this (is working for other forms but without this options field.
def addKunder(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = AddKunder(request.user.id, request.POST)
# check whether it's valid:
if form.is_valid():
form.save()
return HttpResponseRedirect('/pyOekonomi/kunder/')
# if a GET (or any other method) we'll create a blank form
else:
print ('somethin goes wrong')
form = AddKunder(request.user.id)
return render(request, 'pyOekonomi/modules/kunder/add.html', {'form': form})
Forms look like:
class AddKunder(forms.ModelForm):
Firma = forms.CharField(widget=forms.TextInput(attrs={'class': 'input_app', 'autofocus': True}),
label='Firma',
required=False)
Adresse = forms.CharField(widget=forms.TextInput(attrs={'class': 'input_app'}),
label='Adresse',
required=False)
Postnummer = forms.IntegerField(widget=forms.TextInput(attrs={'class': 'input_app'}),
label='Postcode',
required=False)
Byen = forms.CharField(widget=forms.TextInput(attrs={'class': 'input_app'}),
label='By',
required=False)
Land = forms.ModelChoiceField(widget=forms.Select(attrs={'class': 'countryselectbox'}),
queryset=CountryList.objects.values_list('countryname', flat=True).order_by('code'),
initial='Denmark',
to_field_name='countryname',
required=False)
CVRCountrycode = forms.ModelChoiceField(widget=forms.Select(attrs={'class': 'countryselectbox'}),
label='CVR Landkode',
queryset=CountryList.objects.values_list('code', flat=True).order_by('code'),
initial='DK',
to_field_name='code',
required=False)
FirmaID = forms.ModelChoiceField(queryset=None,
widget=forms.Select(attrs={'class': 'selectbox'}),
label='Firma',
to_field_name='ID',
empty_label=None)
#, queryset=FirmaModule.objects.values_list('Firmanavn', flat=True).filter(UserID=self.user).order_by('Firmanavn'), to_field_name='ID', empty_label=None)
class Meta:
model = FirmaModule
fields = ['Firmanavn', 'ID']
def __init__(self, user, *args, **kwargs):
super(AddKunder, self).__init__(*args, **kwargs)
self.fields['FirmaID'].queryset = FirmaModule.objects.filter(UserID=user)
#active = forms.BooleanField()
CVR = forms.IntegerField(widget=forms.TextInput(attrs={'class': 'input_app'}),label='CVR Nummer', required=False)
Kundenummer = forms.CharField(widget=forms.TextInput(attrs={'class': 'input_app' }), label='Kundenummer', required=True)
class Meta:
model = Kunder
labels = {
'Byen': 'By',
'CVRCountrycode': 'Landekode',
'CVR': 'CVR Nummer',
'active': 'er aktiv',
'IsFirmaKunde': 'Firmakunde',
'FirmaID': 'Firma',
'EMail': 'E-Mail Adresse'
}
fields = ['Kundenummer', 'FirmaID', 'Efternavn', 'Fornavn', 'Adresse', 'Telefon', 'Postnummer', 'Byen', 'Land', 'CVRCountrycode', 'CVR', 'active', 'IsFirmaKunde', 'UserID', 'EMail', 'Firma']
models:
class Kunder(models.Model):
ID = models.AutoField(primary_key=True)
Kundenummer = models.IntegerField('Kundenummer', unique=True)
Firma = models.CharField('Firma', max_length=128, null=True)
Efternavn = models.CharField('Efternavn', max_length=128)
Fornavn = models.CharField('Fornavn', max_length=128)
FirmaID = models.IntegerField('FirmaID')
UserID = models.IntegerField('UserID')
Adresse = models.CharField('Adresse', max_length=128, null=True)
Telefon = models.CharField('Telefon', max_length=32, null=True)
Postnummer = models.IntegerField('Postnummer', null=True)
Byen = models.CharField('Byen', max_length=100, null=True)
Land = models.CharField('Land', max_length=32, null=True)
CVRCountrycode = models.CharField('CVRCountrycode', max_length=3, null=True)
CVR = models.IntegerField('CVR', null=True)
active = models.BooleanField(default = False)
IsFirmaKunde = models.BooleanField(default = False)
EMail = models.EmailField('EMail', max_length=128, null=True)
def __str__(self):
return self.Firma
I fixed it myself.
Changed Model from
FirmaID = models.IntegerField('FirmaID')
to
FirmaID = models.ForeignKey('FirmaModule', on_delete=models.CASCADE,)
When the user is required to fill his profile, he picks a city from the Google Places Autocomplete and posts the form, in the view I extract the city Id from the Google API based on the posted text (I use the same id as pk in my db) and try to extract a city from my db.
These are the models:
class City(models.Model):
#extracted from the Google API
city_id = models.CharField(primary_key=True, max_length=150)
name = models.CharField(max_length=128, blank=True)
country = models.CharField(max_length=128, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', primary_key=True)
city = models.ForeignKey(City, blank=True, null=True)
prof_pic = models.ImageField(blank=True, upload_to='profile_pictures')
This is the view:
def createprofile(request):
if request.method =='POST':
user = User.objects.get(username=request.user.username)
user_form = UserForm(data=request.POST, instance=user)
profile_form = UserProfileForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
#brings back the city search result as text
searched_city = request.POST['city']
#brings back city ID from the Google API
searched_city_id = population_script.get_city_json(searched_city.replace(" ", ""))['results'][0]['id']
#If it's a valid city
if searched_city_id != -1:
city = City.objects.get(city_id = searched_city_id)
profile.city = city#this is what I want to happen!
else:
return HttpResponse("There's no such city, please try a different query.")
if 'prof_pic' in request.FILES:#now save the profile pic
profile.prof_pic = request.FILES['prof_pic']
print("PROF PIC IS: " + profile.prof_pic.url)
else:
profile.prof_pic = 'images/anon.png'
profile.save()
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
print (user_form.errors, profile_form.errors)
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render(request,
'excurj/createprofile.html', {'user_form':user_form, 'profile_form':profile_form})
However, I keep receiving an error that what's been posted is just text while the city needs to be a City object. I can save the profile pic ok though.
Cannot assign "'Dubai - United Arab Emirates'": "UserProfile.city"
must be a "City" instance.
edit: these are the forms:
class UserForm(forms.ModelForm):
first_name = forms.CharField(
label = "First Name:",
max_length = 80,
required = True
)
last_name = forms.CharField(
label = "Last Name:",
max_length = 80,
required = True,
)
class Meta:
model = User
fields = ('first_name', 'last_name')
class UserProfileForm(forms.ModelForm):
city = forms.CharField(
label = "Your Current City:",
max_length = 200,
required = True,
)
class Meta:
model = UserProfile
fields = ('city','prof_pic', 'dob', 'sex', 'education', 'career', 'about_you',
'music_movies_books', )
Please provide a related_name to the city field in the UserProfile.
I worked around this by creating a new UserProfile field called city_search_text which saves the searched text thus it of course does not return any error. I then receive it in the POST request and comfortable pull the proper city in the view.
I handled a similar issue by overriding my forms' clean method. Something like the following will work:
def clean(self):
# fix city problem
if self.cleaned_data.get("city") is not None:
self.cleaned_data['city'] = City.objects.get(id=self.cleaned_data.get("city"))
return self.cleaned_data
I have a PatientRegistrationForm and a PatientBillingForm form in a single view RegisterPatient.
When when I submit the patient form (form), the submitted date is stored in the database, nut the billing form (form1) only updates the staff and patient fields and nothing is stored in the payment_type, amount and receipt_number.
Please can anyone help point out why the second form is not being updated on the database?
Here is the views, models, forms and template code:
views.py
def RegisterPatient(request):
# bills = obj.bill_set.all()
form = PatientRegistrationForm(request.POST or None)
form1 = PatientBillingForm(request.POST or None)
if form.is_valid() and form1.is_valid():
instance = form.save(commit=False)
instance1 = form1.save(commit=False)
payment_type = form1.cleaned_data["payment_type"]
amount = form1.cleaned_data["amount"]
receipt_number = form1.cleaned_data["receipt_number"]
first_bill = Billing()
first_bill.payment_type = payment_type
first_bill.amount = amount
first_bill.receipt_number = receipt_number
# first_bill.saff
# first_bill.patients
print first_bill.payment_type, first_bill.amount, first_bill.receipt_number
first_name = form.cleaned_data["first_name"]
last_name = form.cleaned_data["last_name"]
other_name = form.cleaned_data["other_name"]
phone_number = form.cleaned_data["phone_number"]
new_patient = Patient()
new_patient.patient_number = UniquePatientNumber()
new_patient.first_name = first_name
new_patient.last_name = last_name
new_patient.other_name = other_name
new_patient.save()
first_bill.save()
model.py
class Patient(models.Model):
patient_number = models.CharField(max_length=120, unique = True, )
first_name = models.CharField(max_length = 120)
last_name = models.CharField(max_length=120)
other_name = models.CharField(max_length = 120, blank=True, null=True)
phone_number = models.CharField(max_length=15)
def _unicode_(self):
return self.patient_number
class Billing(models.Model):
staff = models.ForeignKey(MyUser, default=1)
patients = models.ForeignKey(Patient, default=1)
payment_type = models.CharField(max_length=100)
amount = models.DecimalField(max_digits=100, decimal_places=2, default=0.00)
receipt_number = models.CharField(max_length=120)
payment_date = models.DateTimeField(auto_now=False, auto_now_add=True)
def _unicode_(self):
return self.staff.username
def new_user_receiver(sender, instance, created, *args, **kwargs):
if created:
new_patient, is_created = Billing.objects.get_or_create(patients=instance)
post_save.connect(new_user_receiver, sender=Patient)
def new_user_creator(sender, instance, created, *args, **kwargs):
if created:
new_user, is_created = Billing.objects.get_or_create(staff=instance)
post_save.connect(new_user_creator, sender=MyUser)
form.py
class PatientRegistrationForm(forms.ModelForm):
class Meta:
model = Patient
exclude = ["patient_number"]
class PatientBillingForm(forms.ModelForm):
class Meta:
model = Billing
fields = ["payment_type","amount","receipt_number"]
forms.html
<form method="POST action="">{% csrf_token %}
{{ form }}
{{ form1 }}
<input type="submit" value="Submit"/>
</form>
In your view function you always create new Billing to MyUser with pk=1 and Patients with pk=1 because you set the default values in Billing fields default=1. You should remove default=1 and set null=True,blank=True instead.
If I understand your logic. You want to create new Billing information to every new User oder new Patient. I guess that in your view function you want to update or create Billing information to a patient. If so you should call
Billing.objects.filter(patients__pk=new_patient.pk).update(payment_type=payment_type,amount=amount,receipt_number=receipt_number)
after new_patient.save() then you could comment out lines with first_bill.
Update
1) comment out the line Billing.objects.filter(...)
2) comment out post_save.connect(new_user_receiver, sender=Patient) in models.py
3) activate the lines with first_bill again and add:
first_bill.patients=new_patient after new_patient.save()
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.