Django Form Select Field from linked ModelChoiceFields - python

I am building a form for a store that will allow clients to choose a price from a number of choices. The current Django template looks like so and works very well.
<select>
<option>{{ p.equipment_list.season_price.currency }} {{ p.equipment_list.season_price.daily }} per day.</option>
<option>{{ p.equipment_list.season_price.currency }} {{ p.equipment_list.season_price.weekly }} per week.</option>
<option>{{ p.equipment_list.season_price.currency }} {{ p.equipment_list.season_price.monthly }} per month.</option>
</select>
I have a product 'p' that is part of a group called Equipment List. This list has different pricing (season_price) depending on the time of year or through an sale offer. This allows maximum customizability and separation of tasks. Here are the models:
class SeasonPrice(models.Model):
name = models.CharField(_('Season Price'), max_length=300)
slug = models.SlugField(max_length=150, unique=True)
description = models.TextField(blank=True)
currency = models.ForeignKey(Currency)
daily = models.DecimalField(max_digits=10, decimal_places=2)
weekly = models.DecimalField(max_digits=10, decimal_places=2)
monthly = models.DecimalField(max_digits=10, decimal_places=2)
…
class EquipmentList(models.Model):
...
name = models.CharField(_('Equipment List'), max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
season_price = models.ForeignKey(SeasonPrice)
….
class Product(ImageModel):
...
equipment_list = models.ForeignKey(EquipmentList, related_name='Equipment')
name = models.CharField('Name', max_length=300)
slug = models.SlugField(max_length=150, unique=True)
description = models.TextField()
is_active = models.BooleanField(default=True)
quantity = models.IntegerField()
…
#models.permalink
def get_absolute_url(self):
return ('product_detail', (), {'product_slug': self.slug})
….
The form looks something like this:
class ProductAddToCartForm(forms.Form):
...
product_slug = forms.CharField(widget=forms.HiddenInput())
price = forms.ModelChoiceField(EquipmentList)
quantity = forms.IntegerField(widget=forms.TextInput(attrs={'size':'2', 'value':'1', 'class':'quantity'}),
error_messages={'invalid':'Please enter a valid quantity.'},
min_value=1)
...
And the View:
def show_product(request, product_slug, template_name="shop/product_detail.html"):
p = get_object_or_404(Product, slug=product_slug)
page_title = p.name
# need to evaluate the HTTP method
if request.method == 'POST':
postdata = request.POST.copy()
form = ProductAddToCartForm(request, postdata)
if form.is_valid():
cart.add_to_cart(request)
url = urlresolvers.reverse('show_cart')
return HttpResponseRedirect(url)
else:
form = ProductAddToCartForm(request=request, label_suffix=':')
form.fields['product_slug'].widget.attrs['value'] = product_slug
request.session.set_test_cookie()
return render_to_response("shop/product_detail.html", locals(), context_instance=RequestContext(request))
All the examples that I have seen of ModelChoiceField are one ForeignKey deep. From the above, I want to be able to chain-link 2 to 3 ForeignKeys and output a numeral string to be multiplied with the quantity the shopper wants and then place into a shopping cart.
Thanks

I don't think that ModelChoiceField is appropriate for the original <select> you show. You are choosing a currency/decimal combination, not a model instance. Probably, you want to build an ad-hoc ChoiceField.
myform.fields['price'].choices = (
('1::3.50', "EUR 3.50 per day"),
# ...
)
where 1::3.50 can be split() in clean/save to give the currency id and the amount string. Something like that.

Related

Dynamic Queryset Based on Special Model Django

I would like to have a page that contains product and related competitor products with it. I tried, but all the times competitor products stay same. Would you please help me?
models:
class Product(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.CharField(max_length=120)
brand = models.CharField(max_length=120)
product = models.CharField(max_length=120)
price = models.DecimalField(decimal_places=2,max_digits=100)
class Comp_Product(models.Model):
product = models.ForeignKey(Product,on_delete=models.CASCADE)
competitor = models.URLField()
price = models.DecimalField(decimal_places=2,max_digits=100)
change = models.FloatField()
stock = models.BooleanField()
last_update = models.DateField(auto_now_add=True)
views:
class ProductListView (ListView):
model = Comp_Product
context_object_name = 'comp_products'
template_name = 'products.html'
class ProductDetailView (LoginRequiredMixin,DetailView):
model = Product
template_name = 'product.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comp_products'] = Comp_Product.objects.all()
return context
urls.py:
path('product/<int:pk>/', ProductDetailView.as_view(),name='product_detail'),
path('comp_products/', ProductListView.as_view(),name='comp_products'),
Here you say comp_products = All the Comp Products in the database.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
>>> context['comp_products'] = Comp_Product.objects.all()
return context
What do you display in your template? Is it {{comp_products}} or
{% for pr in product.comp_products.all %}
{{ pr }}
{% endfor %}
For this you need related_name="products" in your product field in the Comp_Product model
You may find the template that I wrote:
{%for comp_product in comp_products %}
{{comp_product.competitor}} - {{comp_product.price}} -
{{comp_product.change}} - {{comp_product.stock}} - {{comp_product.last_update}}
{%endfor%}

Filtering Data Based On Foreign Key - Django

I am having an issue dynamically populating form data based on the previous selected field. In my case I have two models one which contains different types of memberships associated to different clubs. Then I have another model which handles registrations for individual clubs.
My problem - when the end-user is ready to sign up the form renders (I already filter out members based on the club they originally selected) but I need to filter the price based on the membership selected (foreign key) of player model.
Below is my model for the membership types:
Model to store clubs available memberships so members can select and pay on registration page
class ClubMemberships(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE)
title = models.CharField(max_length=30, default='')
price = models.DecimalField(default=0.00, max_digits=6, decimal_places=2)
description = models.TextField()
def __str__(self):
return self.title
Here is the model for the registration:
Model to store player information to be used for membership registration
class Player(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE)
membership_title = models.ForeignKey(ClubMemberships, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
dob = models.DateField(max_length=8)
email = models.EmailField(max_length=50)
phone = models.CharField(max_length=12)
mobile = models.CharField(max_length=15)
emergency_contact_name = models.CharField(max_length=40)
emergency_contact_mobile = models.CharField(max_length=15)
address1 = models.CharField(max_length=30)
address2 = models.CharField(max_length=30, default='')
address3 = models.CharField(max_length=30, default='')
town = models.CharField(max_length=30)
county = models.CharField(max_length=30)
country = models.CharField(max_length=30)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
Form for player registration:
Form to accept details for members to register
class PlayerRegistrationForm(forms.ModelForm):
class Meta:
model = Player
fields = '__all__'
labels = {
'dob': 'Date of Birth'
}
widgets = {
'dob': forms.DateInput(attrs={'id': 'datepicker'})
}
def __init__(self, *args, **kwargs):
super(PlayerRegistrationForm, self).__init__(*args, **kwargs)
self.fields['club_id'].widget = forms.HiddenInput()
def load_price(self, request):
membership = request.GET.get('membership_title')
title = ClubMemberships.objects.filter(title=membership)
self.fields['price'].queryset = ClubMemberships.objects.filter(price=title.price)
The load_price is an example of what I am trying to accomplish but cannot get it working. I want the form to check the membership selected in the form then filter the price of that membership and display it in the form.
Here is my form in the browser:
Form
Would really appreciate any help as I cannot incorporate PayPal until I can correctly display the price.
Thanks
This is an example based on car manufacturer and models.
This javascript is looking for when a manufacturer changes in the dropdown list
$("#id_car_make").change(function () {
var url = $("#searchForm").attr("data-models-url"); // get the url of the `load_cities` view
var carMakeId = $(this).val(); // get the selected country ID from the HTML input
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/hr/ajax/load-cities/)
data: {
'car_make': carMakeId // add the country id to the GET parameters
},
success: function (data) { // `data` is the return of the `load_cities` view function
$("#id_car_model").html(data); // replace the contents of the city input with the data that came from the server
}
});
});
it calls this url
path('ajax/load-models/', views.load_models, name="ajax_load_models"),
which calls this view
def load_models(request):
car_make_id = request.GET.get('car_make')
car_models = CarModel.objects.filter(car_make_id=car_make_id).order_by('name')
return render(request, 'leases/partials/car_model_dropdown_list_options.html', {'car_models': car_models})
which uses this template to pass the model drop down to the javascript
<option value="">Model</option>
{% for car_model in car_models %}
<option value="{{ car_model.pk }}">{{ car_model.name }}</option>
{% endfor %}

Django fields in form

I am a beginner in Django, hence this might be a simple issue. But I'm not able to get past this successfully.
This is my models.py
class Category(models.Model):
name = models.CharField(max_length=128)
abbr = models.CharField(max_length=5)
def __unicode__(self):
return self.name
class Fabric(models.Model):
name = models.CharField(max_length=128)
abbr = models.CharField(max_length=5)
def __unicode__(self):
return self.name
class Manufacturer(models.Model):
name = models.CharField(max_length=128)
location = models.CharField(max_length=128)
name_abbr = models.CharField(max_length=5, default=None)
loc_abbr = models.CharField(max_length=5, default=None)
def __unicode__(self):
return self.name
class Images(models.Model):
design_id = models.CharField(max_length=128)
file = models.ImageField(upload_to='images')
cost_price = models.FloatField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
fabric = models.ForeignKey(Fabric, on_delete=models.CASCADE)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
selling_price = models.FloatField()
aliveness = models.IntegerField()
date_added = models.DateTimeField(default=datetime.datetime.now)
set_cat = models.IntegerField()
set_cat_no = models.IntegerField()
set_cat_name = models.CharField(max_length=50, blank=True)
I'm building an apparel management database system which contains cloth designs.
My forms.py is
class ImagesForm(forms.ModelForm):
class Meta:
model = Images
fields = ('file','cost_price','set_cat_no','set_cat_name',)
My views.py
#login_required
def uploadphoto(request):
context = RequestContext(request)
context_dict = {}
if request.method == 'POST':
form = ImagesForm(request.POST,request.FILES)
if form.is_valid():
image = form.save(commit=False)
image.save()
return render_to_response(request,'cms/upload-photo.html', {'upload_image': form})
else:
print form.errors
else:
form = ImagesForm()
context_dict = {'upload_image': form}
return render_to_response('cms/upload-photo.html',context_dict, context)
My upload-photo.html is
{% block main %}
<form id="upload_form" method="post" action="/zoomtail/upload-photo/" enctype="multipart/form-data">
{% csrf_token %}
{{ upload_image }}
</form>
{% endblock %}
The problem here is when I goto /upload-photo/ I don't see the drop-downs to the foreign keys to categories, fabric and manufacturers. I have read that it should be automatically generated but I don't see any.
And selling_price has to be calculated based on the given percentage increase from the cost_price which has to be entered in the form. How do I do this? And aliveness of an apparel has to be set by default as 1. How to do this?
set-cat field of an apparel is 1 if it belongs to a set and 2 if it belongs to a catalogue. How do I get a radio button asking whether set or catalogue and that to be captured as an integer in the database?
And design-id field of an apparel has to be a string which contains abbreviations of category, fabric, manufacturer, etc etc all the fields it belongs to. How do I do this dynamically?
I know, this is a very long question but I'm a newbie and these are literally giving me headache since days. I shall be very thankful to those who answer this.
I believe the issue with the dropdown is that you've excluded the fields from ImageForm. You have:
fields = ('file','cost_price','set_cat_no','set_cat_name',)
but should have:
fields = ('file','cost_price','set_cat_no','set_cat_name', 'category', 'fabric', 'manufacturer,)`
if that doesn't work, are there any options in your database for Categories, Fabric, and Manufacturer? If your tables are empty, the dropdown will be empty. If there are values in the database, is there HTML being generated but the label value is blank (i.e. <option>{this is blank}</option>)? In django, you can override the __str__ function to specify how the dropdown options get labeled
Override __str__ as follows:
class Category(models.Model):
name = models.CharField(max_length=128)
abbr = models.CharField(max_length=5)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
You can compute the value of selling_price and any other computed value in the block if request.method == 'POST'.
Example:
def uploadphoto(request):
context = RequestContext(request)
context_dict = {}
if request.method == 'POST':
form = ImagesForm(request.POST,request.FILES)
#- Calculate value(s) here -#
if form.is_valid():
image = form.save(commit=False)
image.save()`
Please see this post here for using radio buttons
You would do this in the same place as #2 above

Unable to automatically pick foreign key from modelform

I am working on a product app on Python 2.7 / Django 1.7.
I have a model for product namely 'product_profile' and I want to allow my customer (end user) to ask any thing regarding specific products using a form.
However I am unable to allow user to automatically select the product (foreign key) and the customer has to select from a drop-down which quite irrational. I have also assigned the foreign key in url-variable.
here is my code:
MODEL.PY
class ProductProfile(models.Model):
category = models.ForeignKey(Category)
brand = models.ForeignKey(Brand)
product_name = models.CharField(max_length=128)
model_name = models.CharField(max_length=128)
generation = models.CharField(max_length=128)
processor = models.CharField(max_length=128)
ram = models.DecimalField(max_digits=2, decimal_places=0)
hdd = models.DecimalField(max_digits=6, decimal_places=2)
optical_drive = models.CharField(max_length=128)
display = models.CharField(max_length=128)
card_reader = models.CharField(max_length=128)
blue_tooth = models.CharField(max_length=128)
web_cam = models.CharField(max_length=128)
warranty = models.CharField(max_length=128)
price = models.DecimalField(max_digits=9, decimal_places=2)
condition = models.TextField()
product_image = models.ImageField(upload_to=update_Product_image_filename)
post_date = models.DateTimeField(db_index=True, auto_now_add=True)
# Override th __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.product_name
class Customer_ps_contact(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(max_length=75)
subject = models.CharField(max_length=128 )
product = models.ForeignKey(ProductProfile)
message = models.TextField()
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format:
'+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], blank=True, max_length=15) # validators should be a
list
def __unicode__(self):
return self.name
FORM.PY
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
product = forms.ModelChoiceField(queryset=ProductProfile.objects.all(),
widget=forms.HiddenInput())
fields = ('name','email', 'product','subject','message', 'phone_number')
VIEWS.PY
def product_inquiry(request, product_id):
product = ProductProfile.objects.get(pk=product_id)
if request.method == 'POST':
#form = Customer_ps_contactForm(request.POST, initial = {'product': product})
#form = Customer_ps_contactForm(initial = {'product': product.id})
form = Customer_ps_contactForm(request.POST)
if form.is_valid():
form_data_dict = form.cleaned_data
print form_data_dict['product']
mail_customer_enquriy(form_data_dict) # Function to send email to admin
thank_u_customer(form_data_dict) # Function to send email to customers
form = form.save(commit=False)
form.product = product
form.save()
return home(request)
else:
print ("form is not valid")
print (form.errors)
else:
form = Customer_ps_contactForm()
context_dict = {'form':form, 'product': product}
return render(request, 'product/product_inquiry2.html',context_dict)
URL Patterns
urlpatterns = patterns('',
url(r'^inquiry/(?P<product_id>\d+)/$', views.product_inquiry, name='price'), # Only relevent url given
)
Template : product_inquiry2.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block body_block %}
{% block title %}Product Inquiry{% endblock %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h2 style="font-weight:bold">Enquiry regarding '{{product.product_name}}'</h2>
<hr>
<form id="contact_form" method="post" action=""/>
{% csrf_token %}
{{ form | crispy }}
<input class="btn btn-primary pull-right " type="submit" name="submit" value="Submit the Message" />
</form>
</div>
</div>
{% endblock %}
What should I do?
You know what the product is from the id in the url, so there's no need to include it in your form.
To check that the product exists in the database, you can use the get_object_or_404 shortcut.
def product_inquiry(request, product_id):
product = get_object_or_404(ProductProfile, pk=product_id)
Then leave out 'product' from your list of fields, and remove the ModelChoiceField with hidden input widget.
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
fields = ('name','email','subject','message','phone_number')
You are already setting the product when you save it, but it would be clearer to use the variable name instance to make it clearer what's going on. If you change your mail_customer_enquriy and thank_u_customer methods to use the instance instead of cleaned_data, then you won't have to do anything with form.cleaned_data.
if form.is_valid():
instance = form.save(commit=False)
instance.product = product
instance.save()
mail_customer_enquriy(instance) # Function to send email to admin
thank_u_customer(instance) # Function to send email to customers
return home(request)

Django template sort by 'unrelated' model's field

I'm trying to sort related items in a template by a field in a model three ForeignKey relationships away. I'm assembling the data for the template in the view as proposed in another StackOverflow answer:
Sorting related items in a Django template
As far as I can tell, I copied the code from this as-is except for I had to change variable names. It doesn't throw an error, it just displays no list items in the HTML unordered list.
# checkout.html
{% for item in cart_items %}
<tr>
<td class="left">
{{ item.name }}
<ul>
{% for part in part_list %}
<li>{{ part.name }}
{% endfor %}
</ul></td>
</tr>
{% endfor %}
And the view...
# views.py
def checkout(request):
cart_items = get_cart_items(request)
itemCollection = []
for item in cart_items:
item.part_list = item.product.buildpart.all().order_by('product__family__type')
itemCollection.append(item)
return render(request, 'checkout.html', locals())
And the get_cart_items function:
# cart.py
def get_cart_items(request):
""" return all items from the current user's cart """
return CartItem.objects.filter(cart_id=get_cart_id(request))
As I said, the template and view are pretty much copies of the solution presented in the aforementioned StackOverflow article. One thing I thought was curious was that itemCollection[] from the view is never referenced in the template.
I believe the order_by clause ('product__family__type') is right only because it doesn't generate an error. But in case that is the problem or a part of it here is the chain of models I am attempting to navigate in that order_by clause:
We start from the shopping cart model (CartItem):
class Item(models.Model):
cart_id = models.CharField(max_length=50)
quantity = models.IntegerField(default=1)
product = models.ForeignKey(PartModel, unique=False)
class Meta:
abstract = True
class CartItem(Item):
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['date_added']
verbose_name = "Cart Item"
Through the 'product' field we get to the model holding our inventory and its self-referential BuildPart ManyToMany model:
class PartModel(models.Model):
family = models.ForeignKey(PartFamily)
name = models.CharField("Model Name", max_length=50, unique=True)
buildpart = models.ManyToManyField('self', through='BuildPart',
symmetrical=False, related_name='+')
class Build(models.Model):
build = models.ForeignKey(PartModel, related_name='+')
part = models.ForeignKey(PartModel, related_name='+')
quantity = models.PositiveSmallIntegerField(default=1)
class Meta:
abstract = True
unique_together = ('build', 'part')
def __unicode__(self):
return self.build.name + ' with ' + str(self.quantity) + ' * ' + \
self.part.family.make.name + ' ' + self.part.name
class BuildPart(Build):
pass
class Meta:
verbose_name = "Build Part"
From there we follow the 'family' field to the PartFamily model:
class PartFamily(models.Model):
make = models.ForeignKey(PartMake)
type = models.ForeignKey(PartType)
name = models.CharField("Family Name", max_length=30,
unique=True)
slug = models.SlugField(unique=True)
And lastly, we get to the model with the 'order' field, the one we wish to sort the related items by, PartType:
class PartType(models.Model):
name = models.CharField("Part Type", max_length=30, unique=True)
slug = models.SlugField(unique=True)
order = models.PositiveSmallIntegerField()
description = models.TextField(blank=True, null=True)
To recap, how do I get the shopping cart products' related items, and sort them by the 'order' field in the PartType model?
You have two errors, both in the template.
Firstly, you've put your items with the sorted relationship in a list called itemCollection, but in the template you're iterating over cart_item instead. This is a very good example of why you should be explicit about what variables you pass to the template, rather than relying on locals().
Secondly, you then iterate over part_list without defining it. You mean item.part_list.

Categories

Resources