Testing Django Inline ModelForms: How to arrange POST data? - python

I have a Django 'add business' view which adds a new business with an inline 'business_contact' form.
The form works fine, but I'm wondering how to write up the unit test - specifically, the 'postdata' to send to self.client.post(settings.BUSINESS_ADD_URL, postdata)
I've inspected the fields in my browser and tried adding post data with corresponding names, but I still get a 'ManagementForm data is missing or has been tampered with' error when run.
Anyone know of any resources for figuring out how to post inline data?
Relevant models, views & forms below if it helps. Lotsa thanks.
MODEL:
class Contact(models.Model):
""" Contact details for the representatives of each business """
first_name = models.CharField(max_length=200)
surname = models.CharField(max_length=200)
business = models.ForeignKey('Business')
slug = models.SlugField(max_length=150, unique=True, help_text=settings.SLUG_HELPER_TEXT)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
phone = models.CharField(max_length=100, null=True, blank=True)
mobile_phone = models.CharField(max_length=100, null=True, blank=True)
email = models.EmailField(null=True)
deleted = models.BooleanField(default=False)
class Meta:
db_table='business_contact'
def __unicode__(self):
return '%s %s' % (self.first_name, self.surname)
#models.permalink
def get_absolute_url(self):
return('business_contact', (), {'contact_slug': self.slug })
class Business(models.Model):
""" The business clients who you are selling products/services to """
business = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=100, unique=True, help_text=settings.SLUG_HELPER_TEXT)
description = models.TextField(null=True, blank=True)
primary_contact = models.ForeignKey('Contact', null=True, blank=True, related_name='primary_contact')
business_type = models.ForeignKey('BusinessType')
deleted = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
address_1 = models.CharField(max_length=255, null=True, blank=True)
address_2 = models.CharField(max_length=255, null=True, blank=True)
suburb = models.CharField(max_length=255, null=True, blank=True)
city = models.CharField(max_length=255, null=True, blank=True)
state = models.CharField(max_length=255, null=True, blank=True)
country = models.CharField(max_length=255, null=True, blank=True)
phone = models.CharField(max_length=40, null=True, blank=True)
website = models.URLField(null=True, blank=True)
class Meta:
db_table = 'business'
def __unicode__(self):
return self.business
def get_absolute_url(self):
return '%s%s/' % (settings.BUSINESS_URL, self.slug)
VIEWS:
def business_add(request):
template_name = 'business/business_add.html'
if request.method == 'POST':
form = AddBusinessForm(request.POST)
if form.is_valid():
business = form.save(commit=False)
contact_formset = AddBusinessFormSet(request.POST, instance=business)
if contact_formset.is_valid():
business.save()
contact_formset.save()
contact = Contact.objects.get(id=business.id)
business.primary_contact = contact
business.save()
#return HttpResponse(help(contact))
#business.primary = contact.id
return HttpResponseRedirect(settings.BUSINESS_URL)
else:
contact_formset = AddBusinessFormSet(request.POST)
else:
form = AddBusinessForm()
contact_formset = AddBusinessFormSet(instance=Business())
return render_to_response(
template_name,
{
'form': form,
'contact_formset': contact_formset,
},
context_instance=RequestContext(request)
)
FORMS:
class AddBusinessForm(ModelForm):
class Meta:
model = Business
exclude = ['deleted','primary_contact',]
class ContactForm(ModelForm):
class Meta:
model = Contact
exclude = ['deleted',]
AddBusinessFormSet = inlineformset_factory(Business,
Contact,
can_delete=False,
extra=1,
form=AddBusinessForm,
)

The problem is you have not included the management form in your data. You need to include form-TOTAL_FORMS (total number of forms in the formset, default is 2), form-INITIAL_FORMS (the initial number of forms in the formset, default is 0) and form-MAX_NUM_FORMS (the maximum number of forms in the formset, default is '').
See the Formset documentation for more information on the management form.

Related

Initial data not working if I hide some one-to-many fields in django

I want to prefill some one to many fields and also hide these field because I want to avoid a scenario where a user can see all the records related to the fields. The problem I'm facing is when I use 'all' on the form fields I the initial data dictionary is working well, but if I try to use a list of the fields I want displayed, the initial data is not getting passed into the form.
Here is my models.py:
class Agent(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = OneToOneField(User, null=True, blank=True, on_delete=models.SET_NULL)
first_name = models.CharField(max_length=15, null=True, blank=True,)
surname = models.CharField(max_length=15, null=True, blank=True,)
provcoord = models.ForeignKey(Provcoord, null=True, blank=True, on_delete=SET_NULL)
regcoord = models.ForeignKey(Regcoord, null=True, blank=False, on_delete=SET_NULL)
region = models.CharField(max_length=15, null=False, blank=True, choices=REGION)
province = models.CharField(max_length=15, null=False, blank=False, choices=PROVINCE)
id_no = id_no = models.CharField(max_length=10, null=False, blank=False, unique=True,)
agent_no = models.CharField(default="Not Assigned", max_length=20, null=False, blank=False)
address = models.TextField(null=False, blank=False)
gender = models.CharField(max_length=20, null=False, blank=False, choices=GENDER)
profile_pic = models.ImageField(upload_to="assets", default="default.png")
is_blacklisted = models.BooleanField(default=False)
reason_for_blacklist = models.TextField(max_length=500, null=True, blank=True)
registered_at = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
return reverse("agent", kwargs={'str' :str.id})
def __str__(self):
return self.user.username
class Adult(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
agent = models.ForeignKey(Agent, null=True, blank=True, on_delete=SET_NULL)
regcoord = models.ForeignKey(Regcoord, null=True, blank=True, on_delete=SET_NULL)
provcoord = models.ForeignKey(Provcoord, null=True, blank=True, on_delete=SET_NULL)
surname = models.CharField(max_length=150, null=False, blank=False)
first_name = models.CharField(max_length=150, null=False, blank=False)
other_name = models.CharField(max_length=150, null=True, blank=True)
address = models.CharField(max_length=200, null=True, blank=True)
region = models.CharField(max_length=15, null=True, blank=True,choices=PROVINCE)
dob = models.CharField(max_length=10, null=False, blank=False)
gender = models.CharField(max_length=20, null=False, blank=False, choices=GENDER)
id_no = models.CharField(max_length=12, null=False, blank=False, unique=True)
receipt_no = models.CharField(max_length=10, default="Not Receipted", null=True,
blank=True)
phone_no = models.CharField(max_length=20, null=False, blank=False,)
marital_status = models.CharField(max_length=20, null=False, blank=False, choices=MARITAL_STATUS)
views.py:
def add_parent(request,):
agent = request.user.agent
regcoord = request.user.agent.regcoord
provcoord = request.user.agent.provcoord
region = request.user.agent.region
province = request.user.agent.province
form = ParentForm(initial={
'agent' :agent,
'regcoord' :regcoord,
'provcoord' :provcoord,
'region' :region,
'province' :province
})
if request.method == 'POST':
form = ParentForm(request.POST, request.FILES,)
if form.is_valid():
form.save()
return redirect('/')
context = {'form' :form}
return render(request, 'kyc/add_adult.html', context)
forms.py:
class ParentForm(ModelForm):
class Meta:
model = Adult
fields = ['surname',
'first_name',
'other_name',
'address',
'dob',
'gender',
'id_no',
'receipt_no',
'phone_no',
'image'
]
Please Help on how I can get around this issue.
Here is an approach I suggest (not tested though):
from django import forms
class ParentForm(ModelForm):
agent = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
regcoord = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
provcoord = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
region = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
province = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(ParentForm, self).__init__(*args, **kwargs)
self.fields['agent'].initial = self.user.agent
self.fields['regcoord'].initial = self.user.regcoord
self.fields['provcoord'].initial = self.user.provcoord
self.fields['region'].initial = self.user.region
self.fields['province'].initial = self.user.province
class Meta:
model = Adult
fields = ['surname',
'first_name',
'other_name',
'address',
'dob',
'gender',
'id_no',
'receipt_no',
'phone_no',
'image'
]
Notes how I referenced the 5 fields (agent, regcoord, provcoord, region, province) as extra fields by declaring them as simple CharFields. So they are no longer rendered from the model as dropdown lists. Then in the method __init__ I define the initial values ​​for each of the fields.
Your function add_parent should become:
def add_parent(request,):
form = ParentForm(user=request.user)
if request.method == 'POST':
form = ParentForm(request.POST, request.FILES,)
if form.is_valid():
form.save()
return redirect('/')
context = {'form' :form}
return render(request, 'kyc/add_adult.html', context)
Edit
Here is another alternative:
def add_parent(request,):
data = {'agent': request.user.agent, 'regcoord': request.user.regcoord, 'provcoord': request.user.provcoord, 'region': request.user.region, 'province': request.user.province}
form = ParentForm(initial=data)
if request.method == 'POST':
form = ParentForm(request.POST, request.FILES,)
if form.is_valid():
form.save()
return redirect('/')
context = {'form' :form}
return render(request, 'kyc/add_adult.html', context)
In the function add_parent, I pass the initial values ​​in the form of a dictionary (data), to the variable initial.
Then you need to remove the __init__ method from your form. Django will take care of rendering the form with the initial values ​​in the corresponding fields.

How Can I Integrate Flutterwave Paymwnt Gate Way with Django

I am working on a Django Project where I want to collect payment in Dollars from Applicants on the portal, and I don't know how to go about it. Though I have been following an online tutorial that shows how to do it but the result I am having is different with the recent error which says 'module' object is not callable.
Remember that I have tested my configured environment and also imported it into my views on top of the page.
Profile model code:
class Profile(models.Model):
applicant = models.OneToOneField(User, on_delete=models.CASCADE, null = True)
surname = models.CharField(max_length=10, null=True)
othernames = models.CharField(max_length=30, null=True)
gender = models.CharField(max_length=6, choices=GENDER, blank=True, null=True)
nation = models.CharField(max_length=255, choices=NATION, blank=True, null=True)
state = models.CharField(max_length=20, null=True)
address = models.CharField(max_length=200, null=True)
phone = models.CharField(max_length=16, null=True)
image = models.ImageField(default='avatar.jpg', upload_to ='profile_images')
def __str__(self):
return f'{self.applicant.username}-Profile'
Education/Referee Model code:
class Education(models.Model):
applicant = models.OneToOneField(User, on_delete=models.CASCADE, null = True)
qualification = models.CharField(max_length=60, choices=INSTITUTE, default=None, null=True)
instition = models.CharField(max_length=40, null=True)
reasons = models.CharField(max_length=100, null=True)
matnumber = models.CharField(max_length=255, null=True)
reference = models.CharField(max_length=100, null=True)
refphone = models.CharField(max_length=100, null=True)
last_updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return f'{self.applicant}-Education'
Submitted Model code:
class Submitted(models.Model):
applicant = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
application = models.UUIDField(primary_key = True, editable = False, default=uuid.uuid4)
confirm = models.BooleanField()
approved = models.CharField(max_length=20, null=True)
date = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
self.application == str(uuid.uuid4())
super().save(*args, **kwargs)
def __unicode__(self):
return self.applicant
def __str__(self):
return f'Application Number: {self.application}-{self.applicant}'
Scholarship Model code:
class Scholarship(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null = True)
name = models.CharField(max_length=100, null = True)
description = models.CharField(max_length=200, null = True)
category = models.CharField(max_length=60, choices=INSTITUTE, default=None, null=True)
amount = models.FloatField()
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'WASU Scholarship: {self.name}-{self.name}'
My View for printing slip:
def AppSlip(request):
check_submited = Submitted.objects.get(applicant=request.user)
check_education = Education.objects.get(applicant = request.user)
candidate_edu = check_education.qualification
scholarship = Scholarship.objects.get(category=candidate_edu)
context = {
'candidate_edu':candidate_edu,
'scholarship':scholarship,
}
return render(request, 'user/slip.html', context)
My view for applicant to fill form for payment which I want their Profile captured automatically in the form:
def scholarship_detail(request, pk):
data = Scholarship.objects.get(id=pk)
if request.method=='POST':
form = PaymentForm(request.POST)
if form.is_valid():
user = Profile.objects.get(applicant=request.user)
name= user.surname
email = form.cleaned_data['email']
amount = form.cleaned_data['amount']
phone = form.cleaned_data['phone']
context = {'applicant':name, 'email':email, 'amount':amount, 'phone':phone}
return process_payment(request, context)
else:
form = PaymentForm()
ctx={
'form':form,
'product':data,
}
return render(request, 'user/scholarship.html', ctx)
My form code for Payment: How can query logged in user profile and fill into name, email, phone, amount from Scholarship Model into amount form filled.
class PaymentForm(forms.Form):
name = forms.CharField(label='Your name', max_length=100)
email = forms.EmailField()
phone=forms.CharField(max_length=15)
amount = forms.FloatField()
View code for processing Payment (Where I am suspecting the error). Though I have configured my env using django-dotenv with the Flutterwave Secret Key in it.
#login_required(login_url='user-login')
def process_payment(request, newContext={}):
auth_token= dotenv('SECRET_KEY')
hed = {'Authorization': 'Bearer ' + auth_token}
data = {
"tx_ref":''+str(math.floor(1000000 + random.random()*9000000)),
"amount":amount,
"currency":"KES",
"redirect_url":"http://localhost:8000/callback",
"payment_options":"card",
"meta":{
"consumer_id":23,
"consumer_mac":"92a3-912ba-1192a"
},
"customer":{
"email":email,
"phonenumber":phone,
"name":name
},
"customizations":{
"title":"WASU Scholarship 2022",
"description":"Best store in town",
"logo":"https://getbootstrap.com/docs/4.0/assets/brand/bootstrap-solid.svg"
}
}
url = ' https://api.flutterwave.com/v3/payments'
response = requests.post(url, json=data, headers=hed)
response=response.json()
link=response['data']['link']
return link
My payment Response View:
#require_http_methods(['GET', 'POST'])
def payment_response(request):
status=request.GET.get('status', None)
tx_ref=request.GET.get('tx_ref', None)
print(status)
print(tx_ref)
return HttpResponse('Finished')
Anticipating your prompt answers. Thanks

Searching foreign key field in django

I have been trying to build a search functionality in my app but i have stuck on querying for the foreign key field, as it doesn't return anything and the code shows no error. Below is my code.
forms.py
class StockSearchForm(forms.ModelForm):
class Meta:
model = Stock
fields = ['category', 'item_name']
My view where i implemented the search
views.py
def list_items(request):
header = 'List of items'
form = StockSearchForm(request.POST or None)
queryset = Stock.objects.all()
context = {
"form": form,
"header": header,
"queryset": queryset,
}
#Searching an item and category
if request.method == 'POST':
queryset = Stock.objects.filter(category__name__icontains=form['category'].value(),
item_name__icontains=form['item_name'].value()
)
context = {
"form": form,
"header": header,
"queryset": queryset,
}
return render(request, "list_items.html", context)
My models are as follows.
models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=50, blank=True, null=True)
def __str__(self):
return self.name
class Stock(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
#category = models.CharField(max_length=50, blank=True, null=True)
item_name = models.CharField(max_length=50, blank=True, null=True)
quantity = models.IntegerField(default='0', blank=True, null=True)
receive_quantity = models.IntegerField(default='0', blank=True, null=True)
receive_by = models.CharField(max_length=50, blank=True, null=True)
issue_quantity = models.IntegerField(default='0', blank=True, null=True)
issue_by = models.CharField(max_length=50, blank=True, null=True)
issue_to = models.CharField(max_length=50, blank=True, null=True)
phone_number = models.CharField(max_length=50, blank=True, null=True)
created_by = models.CharField(max_length=50, blank=True, null=True)
reorder_level = models.IntegerField(default='0', blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=False, auto_now=True)
last_updated = models.DateTimeField(auto_now_add=True, auto_now=False)
export_to_CSV = models.BooleanField(default=False)
def __str__(self):
return self.item_name + '' + str(self.quantity)
So what happens is, I can search just fine the "item_name" field and results come up as required, but when i attempt to search for category no error pops up but no results show up, i kinda feel it's due to some foreign key fields issues but i can't just figure it out, I will much appreciate some help, this thing has been a nightmare for quite a while.
Try doing the following. I assume the form is not being properly used.
if request.method == 'POST' and form.is_valid():
queryset = Stock.objects.filter(category__name__icontains=form.cleaned_data.get('category'),
item_name__icontains=form.cleaned_data.get('item_name')
)
Try this:
queryset=Stock.objects.filter(category__name__icontains=form['category'].value(),
item_name__icontains=form['item_name'].value())

created_by not working with ManyToManyField django

Hello everyone I'm trying top build a task manager web app using django, I need to assign task to one or multiple users I'm using manytomany relation in models.py and in views.py I'm adding created_by user automatically.
My problem is that when I do that I see that no users selected in assigned users but if I add created by user from the form it worked well.
class Task(models.Model):
task_id = models.AutoField(primary_key=True)
shortcode = models.CharField(max_length=15, unique=True, blank=True, null=True)
task_name = models.CharField(max_length=200)
task_progress = models.ForeignKey(TaskProgressStatus, on_delete=models.CASCADE, blank=True, null=True)
customer_name = models.ForeignKey(Customer, on_delete=models.CASCADE, blank=True, null=True)
task_priority = models.ForeignKey(TaskPriority, on_delete=models.CASCADE)
assigned_to_employee = models.ManyToManyField(User)
paid = models.BooleanField(default=False)
on_account = models.BooleanField(default=False)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE, blank=True, null=True)
net_amount = models.DecimalField(decimal_places=2, max_digits=20, blank=True, null=True)
vat = models.IntegerField(default=11)
quote_validity = models.CharField(max_length=200, default='1 Month from offer date')
delivered = models.BooleanField(default=False)
delivered_date = models.DateTimeField(null=True, blank=True)
creation_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
due_date = models.DateTimeField(null=True, blank=True)
created_by = models.ForeignKey(User, related_name='created_by_username', on_delete=models.CASCADE, null=True, blank=True)
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.CASCADE)
file_name = models.FileField(upload_to='projects_files', null=True, blank=True)
notes = models.TextField()
def __str__(self):
return str(self.task_name)
#login_required
def addtask(request):
form = taskForm()
if request.method == 'POST':
form = taskForm(request.POST)
if form.is_valid():
newform = form.save(commit=False)
newform.created_by = request.user
newform.save()
return HttpResponseRedirect(request.path_info)
else:
context = {'form':form}
return render(request, 'tasks/add_task.html', context)
Update
As well pointed out by Ahmed I. Elsayed there is some inconsistency in the title of the question, since the created_by field is actually a ForeignKey, not a ManyToManyField.
That being said, your issue is actually with the foreign key.
My suggestion is to first of all be sure that your form is actually valid. You can do that by printing something inside the if form.is_valid() block.

Populate a Django form field with data from a model

I'm have been struggling on this for 2 days, really. I want to populate Timesheet form field from Employees model as a select field / dropdown list.
Here are my files and I tried so far.
MODEL.PY
class Employees(models.Model):
# MONTHLY = 'MONTHLY'
# SEMIMONTHLY = 'SEMIMONTHLY'
# BIWKEEKLY = 'BIWKEEKLY'
# WEEKLY = 'WEEKLY'
# DAILY = 'DAILY'
PAY_PERIODS = [
('Monthly', 'Monthly'),
('Bi-weekly', 'Bi-weekly'),
('Weekly', 'Weekly'),
('Daily', 'Daily'),
]
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
is_active = models.BooleanField(default=True, verbose_name='Employee is actives')
first_name = models.CharField(max_length=50, verbose_name='First Name.', null=True, blank=False)
middle_name = models.CharField(max_length=50, verbose_name='Middle Name or Initials.', null=True, blank=True)
last_name = models.CharField(max_length=50, verbose_name='Last Name.', null=True, blank=False)
full_name = models.CharField(max_length=50, null=True, blank=True)
phone = PhoneField(blank=True, null=True)
email = models.EmailField(max_length=150, blank=True, null=True)
state = USStateField(null=True, blank=True)
street_address = models.CharField(max_length=150, blank=True, null=True, verbose_name='Street Address.')
zip_code = models.CharField(max_length=50, blank=True, null=True, verbose_name='Zip Code.')
hourly_rate = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
pay_frequency = models.CharField(max_length=100, choices=PAY_PERIODS, blank=True)
hire_date = models.TimeField(auto_now_add=True)
def __str__(self):
return self.full_name
def save( self, *args, **kwargs ):
self.full_name = f'{self.first_name} {self.middle_name} {self.last_name}'
super().save( *args, **kwargs )
class Timesheet(models.Model):
"""A timesheet is used to collet the clock-ins/outs for a particular day
"""
employer = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
full_name = models.ForeignKey(Employees, on_delete=models.CASCADE, null=True, blank=False, verbose_name='Select YOUR Name')
start_date = models.DateField(auto_now_add=True, null=True)
end_date = models.DateField(null=True, blank=False)
time_worked = models.DateField(null=True, blank=False)
def __str__(self):
return self.full_name
VIEWS.PY # I tried both function and class based views
class TimesheetView(CreateView):
model = Timesheet
fields = ('full_name', )
# form_class = TimesheetFrom
# queryset = Employees.objects.filter()
# print(queryset)
template_name = 'users/timesheet.html'
success_url = reverse_lazy('timesheet')
#login_required
def timesheet_view(request):
if request.method == 'POST':
form = TimesheetFrom(request.POST)
if form.is_valid():
emp = form.save(commit=False)
emp.user_id = request.user.pk
emp.save()
return redirect('dashboard')
else:
form = TimesheetFrom()
context = {
'form': TimesheetFrom(),
}
return render(request, 'users/timesheet.html', context)
FORM.PY
class TimesheetFrom(forms.Form):
class Meta:
model = Timesheet
fields = '__all__'
exclude = ('employer', )
#This is the current state of the form but I did tried many approaches.
I did search extensively here (Stackoverflow) but no use case for me. Any help will be greatly appreciated with a cup of coffee.

Categories