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 >
<form method="post" action="{% url 'custupload' %}" enctype="multipart/form-data"; return False;>
{% csrf_token %}
{{ form|crispy }}
<span >
<button type="submit">Continue to Order</button>
</span>
</form>
</span>
</div>
order_details.html
<span >
<form method="post" action="{% url 'orderupload' %}">
{% csrf_token %}
{{ form|crispy }}
<span >
<button type="submit">Review Your Order</button>
</span>
</form>
</span>
</div>
Thanks for the help.
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.
CodePudding user response:
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