選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

views.py 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. import urlparse
  2. from django.shortcuts import get_object_or_404
  3. from django.views.generic import (TemplateView, ListView, DetailView,
  4. CreateView, UpdateView, DeleteView,
  5. FormView, RedirectView)
  6. from django.core.urlresolvers import reverse
  7. from django.core.exceptions import ObjectDoesNotExist
  8. from django.http import HttpResponseRedirect, Http404
  9. from django.contrib import messages
  10. from django.utils.translation import ugettext as _
  11. from django.contrib.auth import (authenticate, login as auth_login,
  12. logout as auth_logout)
  13. from django.contrib.auth.forms import PasswordChangeForm
  14. from django.contrib.sites.models import get_current_site
  15. from django.conf import settings
  16. from django.db.models import get_model
  17. from oscar.views.generic import PostActionMixin
  18. from oscar.core.loading import get_class, get_profile_class, get_classes
  19. Dispatcher = get_class('customer.utils', 'Dispatcher')
  20. EmailAuthenticationForm, EmailUserCreationForm, SearchByDateRangeForm = get_classes(
  21. 'customer.forms', ['EmailAuthenticationForm', 'EmailUserCreationForm',
  22. 'SearchByDateRangeForm'])
  23. ProfileForm = get_class('customer.forms', 'ProfileForm')
  24. UserAddressForm = get_class('address.forms', 'UserAddressForm')
  25. Order = get_model('order', 'Order')
  26. Line = get_model('basket', 'Line')
  27. Basket = get_model('basket', 'Basket')
  28. UserAddress = get_model('address', 'UserAddress')
  29. Email = get_model('customer', 'email')
  30. UserAddress = get_model('address', 'UserAddress')
  31. CommunicationEventType = get_model('customer', 'communicationeventtype')
  32. class LogoutView(RedirectView):
  33. url = '/'
  34. def get(self, request, *args, **kwargs):
  35. auth_logout(request)
  36. response = super(LogoutView, self).get(request, *args, **kwargs)
  37. for cookie in settings.OSCAR_COOKIES_DELETE_ON_LOGOUT:
  38. response.delete_cookie(cookie)
  39. return response
  40. class ProfileUpdateView(FormView):
  41. form_class = ProfileForm
  42. template_name = 'customer/profile-form.html'
  43. def get_form_kwargs(self):
  44. kwargs = super(ProfileUpdateView, self).get_form_kwargs()
  45. kwargs['user'] = self.request.user
  46. return kwargs
  47. def form_valid(self, form):
  48. form.save()
  49. messages.success(self.request, "Profile updated")
  50. return HttpResponseRedirect(self.get_success_url())
  51. def get_success_url(self):
  52. return reverse('customer:summary')
  53. class AccountSummaryView(TemplateView):
  54. template_name = 'customer/profile.html'
  55. def get_context_data(self, **kwargs):
  56. ctx = super(AccountSummaryView, self).get_context_data(**kwargs)
  57. ctx['addressbook_size'] = self.request.user.addresses.all().count()
  58. ctx['default_shipping_address'] = self.get_default_shipping_address(self.request.user)
  59. ctx['default_billing_address'] = self.get_default_billing_address(self.request.user)
  60. ctx['emails'] = Email.objects.filter(user=self.request.user)
  61. ctx['orders'] = self.get_orders(self.request.user)
  62. self.add_profile_fields(ctx)
  63. return ctx
  64. def get_orders(self, user):
  65. return Order._default_manager.filter(user=user)[0:5]
  66. def add_profile_fields(self, ctx):
  67. if not hasattr(settings, 'AUTH_PROFILE_MODULE'):
  68. return
  69. try:
  70. profile = self.request.user.get_profile()
  71. except ObjectDoesNotExist:
  72. profile = get_profile_class()()
  73. field_data = []
  74. for field_name in profile._meta.get_all_field_names():
  75. if field_name in ('user', 'id'):
  76. continue
  77. field = profile._meta.get_field(field_name)
  78. if field.choices:
  79. value = getattr(profile, 'get_%s_display' % field_name)()
  80. else:
  81. value = getattr(profile, field_name)
  82. field_data.append({
  83. 'name': getattr(field, 'verbose_name'),
  84. 'value': value,
  85. })
  86. ctx['profile_fields'] = field_data
  87. ctx['profile'] = profile
  88. def get_default_billing_address(self, user):
  89. return self.get_user_address(user, is_default_for_billing=True)
  90. def get_default_shipping_address(self, user):
  91. return self.get_user_address(user, is_default_for_shipping=True)
  92. def get_user_address(self, user, **filters):
  93. try:
  94. return user.addresses.get(**filters)
  95. except UserAddress.DoesNotExist:
  96. return None
  97. class AccountRegistrationView(TemplateView):
  98. template_name = 'customer/registration.html'
  99. redirect_field_name = 'next'
  100. registration_prefix = 'registration'
  101. communication_type_code = 'REGISTRATION'
  102. def get_logged_in_redirect(self):
  103. return reverse('customer:summary')
  104. def check_redirect(self, context):
  105. redirect_to = context.get(self.redirect_field_name)
  106. if not redirect_to:
  107. return settings.LOGIN_REDIRECT_URL
  108. netloc = urlparse.urlparse(redirect_to)[1]
  109. if netloc and netloc != self.request.get_host():
  110. return settings.LOGIN_REDIRECT_URL
  111. return redirect_to
  112. def get_context_data(self, *args, **kwargs):
  113. context = super(AccountRegistrationView, self).get_context_data(*args, **kwargs)
  114. redirect_to = self.request.REQUEST.get(self.redirect_field_name, '')
  115. context[self.redirect_field_name] = redirect_to
  116. context['registration_form'] = EmailUserCreationForm(
  117. prefix=self.registration_prefix
  118. )
  119. return context
  120. def send_registration_email(self, user):
  121. code = self.communication_type_code
  122. ctx = {'user': user,
  123. 'site': get_current_site(self.request)}
  124. try:
  125. event_type = CommunicationEventType.objects.get(code=code)
  126. except CommunicationEventType.DoesNotExist:
  127. # No event in database, attempt to find templates for this type
  128. messages = CommunicationEventType.objects.get_and_render(code, ctx)
  129. else:
  130. # Create order event
  131. messages = event_type.get_messages(ctx)
  132. if messages and messages['body']:
  133. dispatcher = Dispatcher()
  134. dispatcher.dispatch_user_messages(user, messages)
  135. def get(self, request, *args, **kwargs):
  136. context = self.get_context_data(*args, **kwargs)
  137. if request.user.is_authenticated():
  138. return HttpResponseRedirect(self.get_logged_in_redirect())
  139. self.request.session.set_test_cookie()
  140. return self.render_to_response(context)
  141. def post(self, request, *args, **kwargs):
  142. context = self.get_context_data(*args, **kwargs)
  143. redirect_to = self.check_redirect(context)
  144. registration_form = EmailUserCreationForm(
  145. prefix=self.registration_prefix,
  146. data=request.POST
  147. )
  148. context['registration_form'] = registration_form
  149. if registration_form.is_valid():
  150. self._register_user(registration_form)
  151. return HttpResponseRedirect(redirect_to)
  152. self.request.session.set_test_cookie()
  153. return self.render_to_response(context)
  154. def _register_user(self, form):
  155. """
  156. Register a new user from the data in *form*. If
  157. ``OSCAR_SEND_REGISTRATION_EMAIL`` is set to ``True`` a
  158. registration email will be send to the provided email address.
  159. A new user account is created and the user is then logged
  160. in.
  161. """
  162. user = form.save()
  163. if getattr(settings, 'OSCAR_SEND_REGISTRATION_EMAIL', True):
  164. self.send_registration_email(user)
  165. user = authenticate(
  166. username=user.email,
  167. password=form.cleaned_data['password1']
  168. )
  169. auth_login(self.request, user)
  170. if self.request.session.test_cookie_worked():
  171. self.request.session.delete_test_cookie()
  172. class AccountAuthView(AccountRegistrationView):
  173. template_name = 'customer/login_registration.html'
  174. login_prefix = 'login'
  175. def get_context_data(self, *args, **kwargs):
  176. context = super(AccountAuthView, self).get_context_data(*args, **kwargs)
  177. redirect_to = self.request.REQUEST.get(self.redirect_field_name, '')
  178. context[self.redirect_field_name] = redirect_to
  179. context['login_form'] = EmailAuthenticationForm(prefix=self.login_prefix)
  180. context['registration_form'] = EmailUserCreationForm(prefix=self.registration_prefix)
  181. return context
  182. def post(self, request, *args, **kwargs):
  183. context = self.get_context_data(*args, **kwargs)
  184. redirect_to = self.check_redirect(context)
  185. if u'login_submit' in self.request.POST:
  186. login_form = EmailAuthenticationForm(
  187. prefix=self.login_prefix,
  188. data=request.POST
  189. )
  190. if login_form.is_valid():
  191. auth_login(request, login_form.get_user())
  192. if request.session.test_cookie_worked():
  193. request.session.delete_test_cookie()
  194. return HttpResponseRedirect(redirect_to)
  195. context['login_form'] = login_form
  196. if u'registration_submit' in self.request.POST:
  197. registration_form = EmailUserCreationForm(
  198. prefix=self.registration_prefix,
  199. data=request.POST
  200. )
  201. context['registration_form'] = registration_form
  202. if registration_form.is_valid():
  203. self._register_user(registration_form)
  204. return HttpResponseRedirect(redirect_to)
  205. self.request.session.set_test_cookie()
  206. return self.render_to_response(context)
  207. class EmailHistoryView(ListView):
  208. """Customer email history"""
  209. context_object_name = "emails"
  210. template_name = 'customer/email-history.html'
  211. paginate_by = 20
  212. def get_queryset(self):
  213. """Return a customer's orders"""
  214. return Email._default_manager.filter(user=self.request.user)
  215. class EmailDetailView(DetailView):
  216. """Customer order details"""
  217. template_name = "customer/email.html"
  218. context_object_name = 'email'
  219. def get_object(self):
  220. """Return an order object or 404"""
  221. return get_object_or_404(Email, user=self.request.user, id=self.kwargs['email_id'])
  222. class OrderHistoryView(ListView):
  223. """
  224. Customer order history
  225. """
  226. context_object_name = "orders"
  227. template_name = 'customer/order-history.html'
  228. paginate_by = 20
  229. model = Order
  230. form_class = SearchByDateRangeForm
  231. def get(self, request, *args, **kwargs):
  232. if 'date_from' in request.GET:
  233. self.form = SearchByDateRangeForm(self.request.GET)
  234. if not self.form.is_valid():
  235. self.object_list = self.get_queryset()
  236. ctx = self.get_context_data(object_list=self.object_list)
  237. return self.render_to_response(ctx)
  238. else:
  239. self.form = SearchByDateRangeForm()
  240. return super(OrderHistoryView, self).get(request, *args, **kwargs)
  241. def get_queryset(self):
  242. qs = self.model._default_manager.filter(user=self.request.user)
  243. if self.form.is_bound and self.form.is_valid():
  244. qs = qs.filter(**self.form.get_filters())
  245. return qs
  246. def get_context_data(self, *args, **kwargs):
  247. ctx = super(OrderHistoryView, self).get_context_data(*args, **kwargs)
  248. ctx['form'] = self.form
  249. return ctx
  250. class OrderDetailView(DetailView, PostActionMixin):
  251. """Customer order details"""
  252. model = Order
  253. def get_template_names(self):
  254. return ["customer/order.html"]
  255. def get_object(self):
  256. return get_object_or_404(self.model, user=self.request.user, number=self.kwargs['order_number'])
  257. def do_reorder(self, order):
  258. """
  259. 'Re-order' a previous order.
  260. This puts the contents of the previous order into your basket
  261. """
  262. basket = self.request.basket
  263. # Convert line attributes into basket options
  264. lines_added = []
  265. warnings = []
  266. # Collect lines to be added to the basket and the warnings
  267. for line in order.lines.all():
  268. if not line.product:
  269. warnings.append(_("'%s' unavailable for re-order") % line.title)
  270. continue
  271. try:
  272. basket_line = basket.lines.get(product=line.product)
  273. except Line.DoesNotExist:
  274. desired_qty = line.quantity
  275. else:
  276. desired_qty = basket_line.quantity + line.quantity
  277. is_available, reason = line.product.is_purchase_permitted(
  278. user=self.request.user,
  279. quantity=desired_qty)
  280. if not is_available:
  281. warnings.append(reason)
  282. continue
  283. lines_added.append(line)
  284. # Check whether the number of items in the basket won't exceed the maximum
  285. total_quantity = sum([line.quantity for line in lines_added])
  286. is_quantity_allowed, reason = basket.is_quantity_allowed(total_quantity)
  287. self.response = HttpResponseRedirect(reverse('customer:order-list'))
  288. if not is_quantity_allowed:
  289. messages.warning(self.request, reason)
  290. return
  291. else:
  292. # Add items to the basket, display warnings
  293. for warning in warnings:
  294. messages.warning(self.request, warning)
  295. for line in lines_added:
  296. options = []
  297. for attribute in line.attributes.all():
  298. if attribute.option:
  299. options.append({'option': attribute.option, 'value': attribute.value})
  300. basket.add_product(line.product, line.quantity, options)
  301. if len(lines_added) > 0:
  302. self.response = HttpResponseRedirect(reverse('basket:summary'))
  303. messages.info(self.request,
  304. _("All available lines from order %s "
  305. "have been added to your basket") % order.number)
  306. class OrderLineView(DetailView, PostActionMixin):
  307. """Customer order line"""
  308. def get_object(self):
  309. """Return an order object or 404"""
  310. order = get_object_or_404(Order, user=self.request.user, number=self.kwargs['order_number'])
  311. return order.lines.get(id=self.kwargs['line_id'])
  312. def do_reorder(self, line):
  313. self.response = HttpResponseRedirect(reverse('customer:order',
  314. args=(int(self.kwargs['order_number']),)))
  315. basket = self.request.basket
  316. # Check whether basket items quantity won't exceed the maximum
  317. is_quantity_allowed, reason = basket.is_quantity_allowed(line.quantity)
  318. if not is_quantity_allowed:
  319. messages.warning(self.request, reason)
  320. return
  321. if not line.product:
  322. messages.info(self.request, _("This product is no longer available for re-order"))
  323. return
  324. try:
  325. basket_line = basket.lines.get(product=line.product)
  326. except Line.DoesNotExist:
  327. desired_qty = line.quantity
  328. else:
  329. desired_qty = basket_line.quantity + line.quantity
  330. is_available, reason = line.product.is_purchase_permitted(
  331. user=self.request.user,
  332. quantity=desired_qty)
  333. if not is_available:
  334. messages.warning(self.request, reason)
  335. return
  336. # We need to pass response to the get_or_create... method
  337. # as a new basket might need to be created
  338. self.response = HttpResponseRedirect(reverse('basket:summary'))
  339. # Convert line attributes into basket options
  340. options = []
  341. for attribute in line.attributes.all():
  342. if attribute.option:
  343. options.append({'option': attribute.option, 'value': attribute.value})
  344. basket.add_product(line.product, line.quantity, options)
  345. if line.quantity > 1:
  346. msg = _("%(qty)d copies of '%(product)s' have been added to your basket") % {
  347. 'qty': line.quantity, 'product': line.product}
  348. else:
  349. msg = _("'%s' has been added to your basket") % line.product
  350. messages.info(self.request, msg)
  351. class AddressListView(ListView):
  352. """Customer address book"""
  353. context_object_name = "addresses"
  354. template_name = 'customer/address-book.html'
  355. paginate_by = 40
  356. def get_queryset(self):
  357. """Return a customer's addresses"""
  358. return UserAddress._default_manager.filter(user=self.request.user)
  359. class AddressCreateView(CreateView):
  360. form_class = UserAddressForm
  361. mode = UserAddress
  362. template_name = 'customer/address-form.html'
  363. def get_context_data(self, **kwargs):
  364. ctx = super(AddressCreateView, self).get_context_data(**kwargs)
  365. ctx['title'] = _('Add a new address')
  366. return ctx
  367. def form_valid(self, form):
  368. self.object = form.save(commit=False)
  369. self.object.user = self.request.user
  370. self.object.save()
  371. return HttpResponseRedirect(self.get_success_url())
  372. def get_success_url(self):
  373. messages.success(self.request, _("Address saved"))
  374. return reverse('customer:address-list')
  375. class AddressUpdateView(UpdateView):
  376. form_class = UserAddressForm
  377. model = UserAddress
  378. template_name = 'customer/address-form.html'
  379. def get_context_data(self, **kwargs):
  380. ctx = super(AddressUpdateView, self).get_context_data(**kwargs)
  381. ctx['title'] = _('Edit address')
  382. return ctx
  383. def get_queryset(self):
  384. return UserAddress._default_manager.filter(user=self.request.user)
  385. def get_success_url(self):
  386. messages.success(self.request, _("Address saved"))
  387. return reverse('customer:address-detail', kwargs={'pk': self.get_object().pk })
  388. class AddressDeleteView(DeleteView):
  389. model = UserAddress
  390. def get_queryset(self):
  391. """Return a customer's addresses"""
  392. return UserAddress._default_manager.filter(user=self.request.user)
  393. def get_success_url(self):
  394. return reverse('customer:address-list')
  395. def get_template_names(self):
  396. return ["customer/address-delete.html"]
  397. class AnonymousOrderDetailView(DetailView):
  398. model = Order
  399. def get_template_names(self):
  400. return ["customer/anon-order.html"]
  401. def get_object(self):
  402. order = get_object_or_404(self.model, user=None, number=self.kwargs['order_number'])
  403. if self.kwargs['hash'] != order.verification_hash():
  404. raise Http404()
  405. return order
  406. class ChangePasswordView(FormView):
  407. form_class = PasswordChangeForm
  408. template_name = 'customer/change-password.html'
  409. def get_form_kwargs(self):
  410. kwargs = super(ChangePasswordView, self).get_form_kwargs()
  411. kwargs['user'] = self.request.user
  412. return kwargs
  413. def form_valid(self, form):
  414. form.save()
  415. messages.success(self.request, _("Password updated"))
  416. return HttpResponseRedirect(self.get_success_url())
  417. def get_success_url(self):
  418. return reverse('customer:summary')