I am trying to save form based data in many to many relationship. My models is as follow:
class Student(models.Model):
stuName = models.CharField(max_length=100)
stuCity = models.CharField(max_length=100)
stuPhone = models.IntegerField(max_length=10)
stuNationality = models.CharField(max_length=50)
stuCreatedt = models.DateTimeField(default=timezone.now)
def __str__(self):
return '%s %s %s' % (self.stuName,self.stuCity,self.stuNationality)
class Dept(models.Model):
deptId = models.AutoField(primary_key=True)
deptName = models.CharField(max_length=100)
def __str__(self):
return '%s %s' % (self.deptId, self.deptName)
class Course(models.Model):
courseId = models.AutoField(primary_key=True)
courseName = models.CharField(max_length=100)
enrolledStu = models.IntegerField(max_length=3)
students = models.ManyToManyField(Student)
dept = models.ForeignKey(Dept, on_delete=models.CASCADE)
def __str__(self):
return '%s %s %s %s' % (self.courseName,self.enrolledStu,self.students,self.dept)
which i am trying to save. my view is
def addStudent(request):
if request.method == "POST":
form = CourseForm(request.POST)
if form.is_valid():
print(form.cleaned_data)
course = form.save(commit=False)
course.courseName = request.courseName
course.save()
form.save_m2m()
return redirect('lstDetail')
i tried without form.save_m2m() method but still its giving error.
AttributeError at /stuApp/new/
'WSGIRequest' object has no attribute 'courseName'
Request Method: POST
Request URL: http://127.0.0.1:8000/stuApp/new/
Django Version: 1.11.10
Exception Type: AttributeError
Exception Value:
WSGIRequest' object has no attribute 'courseName
Exception Location: C:\Users\PycharmProjects\learning\stuApp\views.py in addStudent, line 22
Python Executable: C:\Users\PycharmProjects\learning\venv\Scripts\python.exe
Python Version: 3.6.4
this is from html page. on console there isn't any error its just prints the query.
basically i am unable to save data with many to many fields and relationship.
my html is
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
urls py:
urlpatterns = [
url(r'^$',views.listStudent,name='liststudent'),
url(r'^stuApp/(?P<pk>\d+)/$', views.lstDetail, name='lstDetail'),
url(r'^stuApp/new/$', views.addStudent, name='addStudent'),
url(r'^stuApp/new/$', views.addStudent, name='addStudent'),
]
thank you for your help and time
Related
I am currently building a website that will allow the sale of mixing and mastering services. As it is a small set of services, I don't need a shopping cart or any elaborate form of ordering. Instead, I would like a customer details page (which informs my 'Customer' model), an order page where the customer selects what exactly they will be purchasing and uploads any relelvent files (which also informs my 'Order' model), and finally sends the customer to a stripe checkout page.
Currently, the Custome rdetails form is up and running and saving the data to the appropriate database model. Once they click continue, I am struggling to understand how to store the primary key of the Customer instance the user created upon filling out the form, and saving this data in the next form through the foreign key relationship.
Similarly, before being sent to Stripe checkout, I would like to create an 'Order Review' page, reviewing the details of their order. I'm not sure how to pull the primary key of the Order intance that was just created in order to for a Model view on the subsequent page. I believe what I;m missing in order to achieve either of these things is how to get the primary key of the database intsance created by the customer upon submitting the form.
Here is the code relevant to my question, incase I am going about this fundamentally wrong:
models.py
class Customer(models.Model):
first_name = models.CharField(max_length=200, null=False)
last_name = models.CharField(max_length=200, null=False)
phone = models.CharField(max_length=10)
email = models.EmailField(null=False)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.last_name
class Product(models.Model):
MIXMAS = 'Mixing and Mastering Package'
MASO = 'Mastering Only'
FEAT = 'Request a Feature'
TUT = 'Request a Tutor'
NONE = 'Select an option'
PRODUCT_NAME_CHOICES = [
(MIXMAS, 'Mixing and Mastering Package'),
(MASO, 'Mastering Only'),
(FEAT, 'Request a Feature'),
(TUT, 'Request a Tutor'),
(NONE, 'Select an option')
]
name = models.CharField(max_length=100, choices=PRODUCT_NAME_CHOICES, default=NONE)
stripe_product_id = models.CharField(max_length=100)
product_description = models.CharField(max_length=300, null=True)
def __str__(self):
return self.name
class Price(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="prices")
stripe_price_id = models.CharField(max_length=100)
price = models.IntegerField(default=0) # cents
price_description = models.CharField(max_length=300, null=True)
class Meta:
ordering = ['price']
def get_display_price(self):
return "{0:.2f}".format(self.price / 100)
def __str__(self):
return '%s %s %s %s' % ("$", self.price, "-", self.price_description)
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name='Package Type: ')
price = models.ForeignKey(Price, on_delete=models.CASCADE, verbose_name="Number of stems: ")
cust_requests = models.TextField(max_length=500, null=True, verbose_name='Enter any specific requests here: (Leave blank if none): ')
reference_track = models.CharField(max_length=200, null=True, verbose_name='Reference Track (Leave blank if none): ')
music_file = models.FileField(upload_to='studio_orders/', verbose_name="Upload zipped music file: ")
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
order_date = models.DateTimeField(auto_now_add=True)
forms.py
from .models import Order, Customer, Product, Price
from django import forms
from django.urls import reverse_lazy
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from dynamic_forms import DynamicField, DynamicFormMixin
class OrderForm(DynamicFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
def price_choices(form):
product = form['product'].value()
return Price.objects.filter(product=product)
def initial_price(form):
product = form['product'].value()
return Price.objects.filter(product=product).first()
product = forms.ModelChoiceField(
queryset=Product.objects.all(),
initial=Product.objects.first(),
label= "Select a Product:",
widget= forms.RadioSelect(
attrs={
'hx-get' : 'prices',
'hx-target' : '#prices',
'hx-swap' : 'innerHTML'
}),
required=True,
)
prices = DynamicField(
forms.ModelChoiceField,
queryset=price_choices,
initial=initial_price,
label= "Select a price:"
)
cust_requests = forms.CharField(
label = 'Enter any specific requests here: (Leave blank if none): ',
required=False,
max_length=500
)
reference_track = forms.FileField(
label = 'Upload a reference track, if applicable.',
required=False,
)
music_file = forms.FileField(
label = 'Upload your project here. Please ensure project has been zipped prior to uploading.',
required=True
)
class Meta:
model= Order
fields = ['product', 'prices', 'cust_requests', 'reference_track', 'music_file']
class CustomerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
class Meta:
model = Customer
fields = ['first_name', 'last_name', 'phone', 'email']
urls.py
urlpatterns = [
path('', views.StudiosOverview.as_view(), name='musicstudios'),
path('order-details/', views.orderdetails, name='orderdetails'),
path('customer-details/', views.CustomerDetails.as_view(), name='custdetails'),
path('customer-details/upload', views.custupload, name='custupload'),
path('order-details/prices/', views.prices, name='prices'),
path('order-details/upload', views.orderupload, name='orderupload'),
path('cancel/', CancelView.as_view(), name='cancel'),
path('success/', SuccessView.as_view(), name='success'),
path('create-checkout-session/<int:pk>', CreateCheckoutSessionView.as_view(), name='create-checkout-session')
]
views.py
def orderdetails(request):
form = OrderForm()
context = {'form' : form}
template_name = 'musicstudios/order_details.html'
return render(request, template_name, context)
def prices(request):
form = OrderForm(request.GET)
return HttpResponse(form['prices'])
def custupload(request):
if request.POST:
form = forms.CustomerForm(request.POST, request.FILES)
success_url = reverse_lazy('orderdetails')
print(request.FILES)
if form.is_valid():
form.save()
else:
ctx = {'form' : form}
return HttpResponseRedirect(request, 'musicstudios/customer_details.html', ctx)
return HttpResponseRedirect(success_url)
def orderupload(request):
if request.POST:
form = OrderForm()
success_url = reverse_lazy('create-checkout-session')
if form.is_valid():
form.save()
else:
ctx = {'form' : form}
return render(request, 'musicstudios/order_details.html', ctx)
return reverse_lazy(success_url)
class StudiosOverview(View):
def get_context_data(self, **kwargs):
product = Product.objects.all()
prices = Price.objects.all()
context = super(StudiosOverview, self).get_context_data(**kwargs)
context.update({
"product": product,
"prices": prices
})
return context
def get(self, request):
context = {
'page_headline' : 'Studio Services'
}
context.update(sidebar_context)
return render(request, 'musicstudios/overview.html', context)
class CustomerDetails(CreateView):
form_class = forms.CustomerForm
template_name = 'musicstudios/customer_details.html'
stripe.api_key = settings.STRIPE_SECRET_KEY
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
product_id = self.kwargs["pk"]
product = Product.objects.get(id=product_id)
domain = "https://lewnytoonsstudios.com"
if settings.DEBUG:
domain = "http://127.0.0.1:8000"
checkout_session = stripe.checkout.Session.create(
line_items=[
{
# Provide the exact Price ID (for example, pr_1234) of the product you want to sell
'price': product.prices.stripe_price_id,
'quantity': 1,
},
],
mode='payment',
success_url=domain + '/success.html',
cancel_url=domain + '/cancel.html',
automatic_tax={'enabled': True},
)
return JsonResponse({
'id' : checkout_session.id
})
class SuccessView(TemplateView):
template_name = "success.html"
class CancelView(TemplateView):
template_name = "cancel.html"
Relevant HTML templates:
customer_details.html
<span class="flex-auto flex-col">
<form method="post" class="py-2" action="{% url 'custupload' %}" enctype="multipart/form-data"; return False;>
{% csrf_token %}
{{ form|crispy }}
<span class="flex justify-end">
<button class="lewny_button my-4" type="submit">Continue to Order</button>
</span>
</form>
</span>
</div>
order_details.html
<span class="flex-auto flex-col">
<form class="py-2" method="post" action="{% url 'orderupload' %}">
{% csrf_token %}
{{ form|crispy }}
<span class="flex justify-end">
<button class="lewny_button my-4" type="submit">Review Your Order</button>
</span>
</form>
</span>
</div>
I have tried several htmx methods of 'getting' the object but have been unable to achieve anything that works. I considered grabbing the most recent object from the database, but this seemed like a very insecure way to go about the solution.
This would seem a job for session variables. Once your customer is created by your save function, you can grab the id and place it in a session variable for later reference.
if form.is_valid():
customer = form.save()
request.session['customer_id'] = customer.id
You can access this wherever you need, either as request.session['customer_id'] (or request.sessions.get('customer_id') to return None if not set) in a functional view or self.request as above in a class based view.
More info in the docs
I am trying to get data based on primary key and display for EDIT in html page. but i am getting 'QuerySet' object has no attribute '_meta' error.
I try to resolve it by looking at other posts but unable to do so.. hope somebody will help in problem.
my forms:
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ('stuName','stuCity','stuPhone','stuNationality','stuCreatedt')
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ('courseId','courseName','enrolledStu','students','dept')
class DeptForm(forms.ModelForm):
class Meta:
model = Dept
fields = ('deptId','deptName')
Models.py
class Student(models.Model):
stuName = models.CharField(max_length=100)
stuCity = models.CharField(max_length=100)
stuPhone = models.IntegerField(max_length=10)
stuNationality = models.CharField(max_length=50)
stuCreatedt = models.DateTimeField(default=timezone.now)
def __str__(self):
return '%s %s %s' % (self.stuName,self.stuCity,self.stuNationality)
Class Dept :
class Dept(models.Model):
deptId = models.AutoField(primary_key=True)
deptName = models.CharField(max_length=100)
def __str__(self):
return '%s %s' % (self.deptId, self.deptName)
class Course
class Course(models.Model):
courseId = models.AutoField(primary_key=True)
courseName = models.CharField(max_length=100)
enrolledStu = models.IntegerField(max_length=3)
students = models.ManyToManyField(Student)
dept = models.ForeignKey(Dept, on_delete=models.CASCADE)
def __str__(self):
return '%s %s %s %s' % (self.courseName,self.enrolledStu,self.students,self.dept)
urls.py for edit is
url(r'^stuApp/(?P\d+)/$', views.edtStudent, name='edtStudent'),
method for edit inside view.py is :
def edtStudent(request,pk):
course = Course.objects.filter(pk=1).prefetch_related('students').select_related('dept')
if request.method =="POST":
form = CourseForm(request.POST,instance=Course)
if form.is_valid():
course = form.save(commit=False)
course.courseName = request.POST['courseName']
course.enrolledStu = request.Post['enrolledStu']
course.save()
course.save_m2m()
return redirect('liststudent')
else:
#form = CourseForm()
#return render(request, 'stuApp/edtStudent.html', {'form':form})
form = CourseForm(instance=course)
return render_to_response('edtStudent.html', {'form': form})
Html is :
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
it displays log from else but somehow doesn't display anything on html..
Thank you for your time....
filter always returns a queryset. But you need to pass a model instance, not a queryset, to the form. Use get instead.
course = Course.objects.filter(pk=1).prefetch_related('students').select_related('dept').get()
Sorry for my poor english...
I've got a Model called Habitation :
class Habitation(models.Model):
propr = models.ForeignKey(Client, related_name="proprietaire")
locat = models.ForeignKey(Client, related_name="locataire", null=True, blank=True)
etage = models.CharField(max_length=2, blank=True)
numero = models.CharField(max_length=3, blank=True)
ad1 = models.CharField(max_length=64)
ad2 = models.CharField(max_length=64, blank=True)
cp = models.CharField(max_length=5)
ville = models.CharField(max_length=32)
def get_appareils(self):
return Appareil.objects.filter(habitation=self)
def selflink(self):
if self.id:
return 'Editer' % str(self.id)
else:
return 'Indéfini'
selflink.allow_tags = True
def __unicode__(self):
return u'%s - %s %s' % (self.ad1, self.cp, self.ville)
With his edit view :
def edit(request, habitation_id):
habitation = Habitation.objects.get(pk=habitation_id)
if request.POST:
form = HabitationForm(request.POST, instance=habitation)
if form.is_valid():
form.save()
return redirect('clients')
else:
form = HabitationForm(instance=habitation)
print form.fields
return render_to_response('habitations/edit.html', {
'habitation_id': habitation_id,
'form': form,
}, context_instance=RequestContext(request))
and his template :
<table>
<form action="/habitations/edit/{{ habitation_id }}/" method="post">
{{ form }}
{% csrf_token %}
{{ form.as_table }}
</form>
</table>
Form:
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class meta:
model = models.Habitation
fields = ('propr', 'locat', 'etage', 'numero', 'ad1', 'ad2', 'cp', 'ville',)
My view (or my ModelForm) doesn't retrive any field, so no more form field.
Is anybody has any suggestion ?
The meta class name in form should be Meta not meta.
Update your form to
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class Meta: #<---- define with capital M
model = models.Habitation
fields = ('propr', 'locat', 'tegae', 'numero', 'ad1', 'ad2', 'cp', 'ville',)
I'm really stuck with this. To show my problem I created a new Django project and started from scratch, focusing only on one single form.
What I'm trying to do is to create a form with several fields of the same name. I tried using modelformset_factory to achieve this but it looks to me like it's not what I really need.
Below is my code (also on dpaste) which currently works fine with one single field called name. How can I create and process a form which would have several name fields? Could somebody point me in the right direction?
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
catformInstance = catform.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{{ catform.as_p }}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
UPDATE: to clarify, I want to allow user to insert several categories within one form. I think I'm getting close, here is my new version of views.py but it still stores just one category (the last one in the list):
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
names = request.POST.getlist('name')
catformInstance = catform.save(commit = False)
for name in names:
catformInstance.name = name
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
You cannot have fields with the same name (on the same Model). If you only need to change the html label in the html form, use
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
name2 = models.CharField(max_length=30, unique=True, verbose_name="name")
user = models.ForeignKey(User, blank=True, null=True)
or
class CategoryForm(ModelForm):
def __init__(self , *args, **kwargs):
super(CategoryForm, self).__init__(*args, **kwargs)
self.fields['name2'].label = "name"
Here is a working solution. Thanks to #YardenST for pointing me in the right direction. I managed to solve my initial problem by following this tutorial.
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catforms = [CategoryForm(request.POST, prefix=str(x), instance=Category()) for x in range(0,3)]
if all([cf.is_valid() for cf in catforms]):
for cf in catforms:
catformInstance = cf.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = [CategoryForm(prefix=str(x), instance=Category()) for x in range(0,3)]
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{% for catform_instance in catform %} {{ catform_instance.as_p }} {% endfor %}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
I have the following models: Department, Projects, Departmentprojects, Employees, and Membership. A Department has many Projects and Projects have many Employees that are assigned roles through Membership. I am trying to create a InlineFormset so that an EU can assign an Employee's role to multiple projects.
My template renders the right labels and fields, but the Departmentprojects labels and fields don't show the name of the projects. It only shows "Departmentprojects object". How can I get the form to render the name of the project instead of "Departmentprojects object"?
--
Current Template in Browser:
Departmentproject label: (drop down menu with two options listed as "Departmentprojects object")
Role: Project Manager
Desired Template in Browser:
Departmentproject name 1: Project Manager
Departmentproject name 2: Some other role
--
MODELS:
class Projects(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class Department(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class Employees(models.Model):
name = models.CharField(max_length=15)
def __unicode__(self):
return self.name
class Departmentprojects(models.Model):
department = models.ForeignKey(Department)
projects = models.ForeignKey(Projects)
members = models.ManyToManyField(Employees, through='Membership')
class Membership(models.Model):
departmentprojects = models.ForeignKey(Departmentprojects)
employees = models.ForeignKey(Employees)
role = models.CharField(max_length=20)
--
VIEW
def addtoprojects(request, employees_id):
e = get_object_or_404(Employees, pk=employees_id)
ProjectsInlineFormSet = inlineformset_factory(Employees, Membership, max_num=1)
if request.method == "POST":
formset = ContactInlineFormSet(request.POST, instance=e)
if formset.is_valid():
formset.save()
else:
formset = ProjectsInlineFormSet(instance=e)
return render_to_response('gcstest/contact.html', {'e': e, 'formset': formset}, context_instance=RequestContext(request))
--
TEMPLATE
<form method="post" action="/assign_to_project/{{ employees.id }}/">
{% csrf_token %}
<table>
{{ formset }}
</table>
<input type="submit" value="Submit"/>
</form>
Add a __unicode__ method to your Departmentprojects model e.g.
class Departmentprojects(models.Model):
department = models.ForeignKey(Department)
projects = models.ForeignKey(Projects)
members = models.ManyToManyField(Employees, through='Membership')
def __unicode__(self):
return "%s > %s" % (self.department, self.projects)