I'm not getting a return on the color and size, only the product name is coming.
And yes my request.POST['color'] and request.POST['size'] are receiving the values, but in the form of names and not in id.
def add_to_cart(request, product_id):
product = Product.objects.get(id=product_id)
variation_list = []
if request.method == "POST":
color = request.POST['color']
size = request.POST['size']
try:
variation = Variation.objects.get(product=product, color__name__exact=color, size__name__exact=size)
variation_list.append(variation)
except:
pass
return HttpResponse(variation_list)
HttpResponse
Because HttpResponse is just going to return Raw text you must explicitly say what you want to show, it can't do full objects and it's probably just doing the Models's __str__ method currently
Example:
o = ''
for i in variation_list:
s += '{0},{1},{2}\n'.format(i.name, i.color.name, i.size.name)
return HttpResponse(s)
Or you could just render a template, like normal, with just a loop like:
{% for i in variation_list %}
{{i.name}},{{i.color}}, etc
{% endfor %}
Another Possibility, that I personally use a Json:
d = []
for i in variation_list:
d.append({
'name': i.name,
'color': i.color.name,
'size': i.size.name,
})
import json
return HttpResponse(json.dumps(d), content_type="application/json")
Related
I want to use Ajax in Django to handle the view of my checkout form after it has been submitted. After the form is submitted, I want it to go to :
return HttpResponseRedirect(reverse(str(next_page))+"?address_added=True") , i.e http://127.0.0.1:8000/checkout/?address_added=True
But for some reason, it is not going there. Rather it's being going to http://127.0.0.1:8000/checkout/?csrfmiddlewaretoken=W4iXFaxwpdtbZLyVI0ov8Uw7KWOM8Ix5GcOQ4k3Ve65KPkJwPUKyBVcE1IjL3GHa&address=123+Main+Street&address2=&state=MA&country=USA&zipcode=55525&phone=%28877%29+314-0742&billing=on
As a result, the form data is also not getting saved. I was thinking if it were because of the new version of Django.
What I want to do is that after they submit the place order button, the form is going to be None, i.e disappear and then I would add a credit card form there for payment. But it is not happening. What is wrong here? How can I do this or is there a better way to do this?
My forms.py:
class UserAddressForm(forms.ModelForm):
class Meta:
model = UserAddress
fields = ["address", "address", "address2", "state", "country", "zipcode", "phone", "billing"]
My accounts.views.py:
def add_user_address(request):
try:
next_page = request.GET.get("next")
except:
next_page = None
if request.method == "POST":
form = UserAddressForm(request.POST)
if form.is_valid():
new_address = form.save(commit=False)
new_address.user = request.user
new_address.save()
if next_page is not None:
return HttpResponseRedirect(reverse(str(next_page))+"?address_added=True")
else:
raise Http404
My orders.views.py:
#login_required()
def checkout(request):
try:
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
except:
the_id = None
return redirect(reverse("myshop-home"))
try:
new_order = Order.objects.get(cart=cart)
except Order.DoesNotExist:
new_order = Order(cart=cart)
new_order.cart = cart
new_order.user = request.user
new_order.order_id = id_generator()
new_order.save()
except:
return redirect(reverse("cart"))
try:
address_added = request.GET.get("address_added")
except:
address_added = None
if address_added is None:
address_form = UserAddressForm()
else:
address_form = None
if new_order.status == "Finished":
#cart.delete()
del request.session['cart_id']
del request.session['items_total']
return redirect(reverse("cart"))
context = {"address_form": address_form, "cart": cart}
template = "orders/checkout.html"
return render(request, template, context)
My urls.py:
path('ajax/add_user_address', accounts_views.add_user_address, name='ajax_add_user_address'),
My checkout.html:
<form method="POST" action="{% url 'ajax_add_user_address' %}?redirect=checkout">
{% csrf_token %}
<fieldset class="form-group">
{{ address_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-dark" type="submit">Place Order</button>
</div>
</form>
I would personally split these up in two views, because they do different stuff.
But, if you want to keep it that way, you can do the following:
First of all, because you are making an AJAX Request, you should return a JsonResponse object.
In your view you can render the checkout.html and pass it as a context variable to your json response:
def add_user_address(request):
...
data = dict()
context = {
'address_form': form,
...
}
data['html_form'] = render_to_string("checkout.html",
context,
request=request)
return JsonResponse(data)
And in your $.ajax success function you can do the following
success: function(data) {
// console.log(data);
$("div-to-replace-html").html(data.html_form);
}
The current admin widget for ArrayField is one field, with comma as delimiter, like this (text list):
This isn't ideal because I would have longer texts (even 20 words) and contain commas. I could change the delimiter to be something else but that still doesn't help with unreadable content in admin.
What I would like is having a list of fields, that I can alter in admin. Something similar to the following image
I could use another table to solve this, but I wonder if it's possible to solve it this way.
Unfortunately Django does not ship with a convenient widget for ArrayFields yet. I'd suggest you to create your own. Here is an example for Django>=1.11:
class DynamicArrayWidget(forms.TextInput):
template_name = 'myapp/forms/widgets/dynamic_array.html'
def get_context(self, name, value, attrs):
value = value or ['']
context = super().get_context(name, value, attrs)
final_attrs = context['widget']['attrs']
id_ = context['widget']['attrs'].get('id')
subwidgets = []
for index, item in enumerate(context['widget']['value']):
widget_attrs = final_attrs.copy()
if id_:
widget_attrs['id'] = '%s_%s' % (id_, index)
widget = forms.TextInput()
widget.is_required = self.is_required
subwidgets.append(widget.get_context(name, item, widget_attrs)['widget'])
context['widget']['subwidgets'] = subwidgets
return context
def value_from_datadict(self, data, files, name):
try:
getter = data.getlist
except AttributeError:
getter = data.get
return getter(name)
def format_value(self, value):
return value or []
Here is the widget template:
{% spaceless %}
<div class="dynamic-array-widget">
<ul>
{% for widget in widget.subwidgets %}
<li class="array-item">{% include widget.template_name %}</li>
{% endfor %}
</ul>
<div><button type="button" class="add-array-item">Add another</button></div>
</div>
{% endspaceless %}
A few javascript (using jQuery for convenience):
$('.dynamic-array-widget').each(function() {
$(this).find('.add-array-item').click((function($last) {
return function() {
var $new = $last.clone()
var id_parts = $new.find('input').attr('id').split('_');
var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1)
$new.find('input').attr('id', id);
$new.find('input').prop('value', '');
$new.insertAfter($last);
};
})($(this).find('.array-item').last()));
});
And you would also have to create your own form field:
from itertools import chain
from django import forms
from django.contrib.postgres.utils import prefix_validation_error
class DynamicArrayField(forms.Field):
default_error_messages = {
'item_invalid': 'Item %(nth)s in the array did not validate: ',
}
def __init__(self, base_field, **kwargs):
self.base_field = base_field
self.max_length = kwargs.pop('max_length', None)
kwargs.setdefault('widget', DynamicArrayWidget)
super().__init__(**kwargs)
def clean(self, value):
cleaned_data = []
errors = []
value = filter(None, value)
for index, item in enumerate(value):
try:
cleaned_data.append(self.base_field.clean(item))
except forms.ValidationError as error:
errors.append(prefix_validation_error(
error, self.error_messages['item_invalid'],
code='item_invalid', params={'nth': index},
))
if errors:
raise forms.ValidationError(list(chain.from_iterable(errors)))
if cleaned_data and self.required:
raise forms.ValidationError(self.error_messages['required'])
return cleaned_data
Finally, set it explicitly on your forms:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['foo', 'bar', 'the_array_field']
field_classes = {
'the_array_field': DynamicArrayField,
}
Try to take a look in this one :
Better ArrayField admin widget?
I think is more about a js thing after you have rendered the Array in a different way.
There are already a lot of questions+answers regarding for loops in django, but none of the solutions work for me, so there must be something fundamentally wrong.
I have a dictionary in python/json (tried both) that I want to loop through and print.
Doing the following print a new line for each character
{% for item in data.dict %}
<p>{{item}}</p>
{% endfor %}
so something like this get's printed
{
'
N
o
d
e
'
:
The following code straight up prints nothing
{% for key, values in data.dict.items %}
<p>{{key}}</p>
{% endfor %}
Data is the name of my registered model and object is one of its variables.
In my Views.py I have something similar to this:
Data.objects.create(
dict=theDictIAmPassing
}.save
EDIT
models.py
from django.db import models
class Data(models.Model):
dict1= models.TextField()
dict2 = models.TextField()
dict3 = models.TextField()
dict4 = models.TextField()
views.py
def add(request):
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
ProjectName = form.cleaned_data['ProjectName']
date = form.cleaned_data['date']
folder = form.cleaned_data['folder']
description = form.cleaned_data['description']
myprog = program.program(folder)
createMetrics(myprog)
Entry.objects.create(
ProjectName=ProjectName,
date=date,
folder=folder,
description=description
).save()
return HttpResponseRedirect('/')
else:
form = EntryForm()
return render(request, 'myApp/form.html', {'form': form})
def createMetrics(myprog):
Metrics.objects.create(
dict1=myprog.getDict1(),
dict2=myprog.getDict2(),
dict3=myprog.getDict3(),
dict4=myprog.getDict4()
).save()
Solution found at https://stackoverflow.com/a/7469287/7761401
I needed to rewrite my Data model. Textfield (which I used because I couldn't find anything else that fits) does not suit dictionary types. Instead install django-picklefield and change type to PickledObjectField
from picklefield.fields import PickledObjectField
class Data(models.Model):
dict1 = PickledObjectField()
dict2 = PickledObjectField()
dict3 = PickledObjectField()
dict4 = PickledObjectField()
I have created a custom template tag in django
disqusTag.py:
register = template.Library()
#register.inclusion_tag('polls/questionDetail.html', takes_context=True)
def disqus_sso(context):
DISQUS_SECRET_KEY = getattr(settings, 'DISQUS_SECRET_KEY', None)
if DISQUS_SECRET_KEY is None:
return "<p>You need to set DISQUS_SECRET_KEY before you can use SSO</p>"
DISQUS_PUBLIC_KEY = getattr(settings, 'DISQUS_PUBLIC_KEY', None)
if DISQUS_PUBLIC_KEY is None:
return "<p>You need to set DISQUS_PUBLIC_KEY before you can use SSO</p>"
user = context['user']
if user.is_anonymous():
return ""
data = json.dumps({
'id': user.id,
'username': user.username,
'email': user.email,
})
# encode the data to base64
message = base64.b64encode(data.encode('utf-8'))
# generate a timestamp for signing the message
timestamp = int(time.time())
key = DISQUS_SECRET_KEY.encode('utf-8')
msg = ('%s %s' % (message, timestamp)).encode('utf-8')
digestmod = hashlib.sha1
# generate our hmac signature
sig = hmac.HMAC(key, msg, digestmod).hexdigest()
return dict(
message=message,
timestamp=timestamp,
sig=sig,
pub_key=DISQUS_PUBLIC_KEY,
)
t = get_template('polls/questionDetail.html')
register.inclusion_tag(t)(disqus_sso)
and i am loading the same in my template questionDetail.html as
{% load disqusTag %}
{% disqus_sso %}
but i am getting this error : 'str' object does not support item assignment
can anybody help me why? I know similar questions have been asked on stack overflow, but i went through all of them and none of them helped.
You should have provided the full traceback.
However, I think the issue is with your if user.is_anonymous check - if it's true, you return an empty string. But the return value of an inclusion tag must always be a context dict. You should return an empty dict there instead.
Suppose this is what we want in every page, we'd create "base_template.html"
<title>{% block title %}{{ page_title }}{% endblock %}</title>
{{ page_title }}{% endblock %}
Instead of passing page_title, domain, current_path from every view function such as:
def display_meta(request):
user_meta = request.META.items()
sorted_meta = sorted(user_meta) # a list of tuples
return render_to_response('meta.html', {'sorted_meta': sorted_meta,
'current_path': request.get_full_path(),
'domain': request.get_host(),
'page_title': display_meta.__name__})
# and repeat the dictionary same manner for other views....
#urls.py
('^book_list/$', 'object_get_list', {'model': models.Book}),
A different approach is wrapping view functions
# urls.py
('^book_list/$', views.get_template(views.object_get_list),{'model': models.Book}),
# views.py
def get_template(view, **extrakw):
def wrapview(request, **extrakw):
template_dict = {'current_path': request.get_full_path(), 'domain': request.get_host()}
extrakw['template_dict'] = template_dict
return view(request, **extrakw)
return wrapview
def object_get_list(request, **kwargs):
model = kwargs.pop('model', None)
model_name = model.__name__.lower()
template_dict = kwargs.pop('template_dict', None)
template_dict[model_name] = model.objects.all()
template_dict['page_title'] = model_name +" list"
template_name = '%s.html' %(model_name)
return render_to_response(template_name, template_dict)
Pro: Besides editing htmls, now modification is done in just one view, instead of every view.
Cons: Ugly URLConf and probably error propne too
Attempt 3:
Create a global dictionary just like template_dict I created.
template_dict = {/..../}
def view1()
def view2() ...
Problem: I can't use request.path (or anything has to do with request). This falls back to the previous attempt (wrapper).
But there must be an easier way. What is the proper way of passing global template variables throughout a django site so each view function is now indepenednt of gloabl templat variables?
Thank you for you time.
Use a context processor.
Add the name of your function to TEMPLATE_CONTEXT_PROCESSORS in settings.py.
A simple context processor I use is:
def common_request_parameters(request):
return {'home_login_form': AuthenticationForm(request)}