Attempt to merge master into task/3747/multiple_cards_support
This commit is contained in:
commit
cf00ff6bd8
269 changed files with 8500 additions and 29554 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import decimal
|
||||
import logging
|
||||
from oca.pool import WrongIdError
|
||||
|
||||
from datacenterlight.models import VMPricing
|
||||
from hosting.models import UserHostingKey, VMDetail
|
||||
from opennebula_api.serializers import VirtualMachineSerializer
|
||||
|
||||
|
|
@ -49,17 +51,83 @@ def get_or_create_vm_detail(user, manager, vm_id):
|
|||
return vm_detail_obj
|
||||
|
||||
|
||||
def get_vm_price(cpu, memory, disk_size):
|
||||
def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'):
|
||||
"""
|
||||
A helper function that computes price of a VM from given cpu, ram and
|
||||
ssd parameters
|
||||
|
||||
:param cpu: Number of cores of the VM
|
||||
:param memory: RAM of the VM
|
||||
:param disk_size: Disk space of the VM
|
||||
:param disk_size: Disk space of the VM (SSD)
|
||||
:param hdd_size: The HDD size
|
||||
:param pricing_name: The pricing name to be used
|
||||
:return: The price of the VM
|
||||
"""
|
||||
return (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
||||
try:
|
||||
pricing = VMPricing.objects.get(name=pricing_name)
|
||||
except Exception as ex:
|
||||
logger.error(
|
||||
"Error getting VMPricing object for {pricing_name}."
|
||||
"Details: {details}".format(
|
||||
pricing_name=pricing_name, details=str(ex)
|
||||
)
|
||||
)
|
||||
return None
|
||||
price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) +
|
||||
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||
(decimal.Decimal(disk_size) * pricing.ssd_unit_price) +
|
||||
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
|
||||
cents = decimal.Decimal('.01')
|
||||
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
||||
return float(price)
|
||||
|
||||
|
||||
def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
|
||||
pricing_name='default'):
|
||||
"""
|
||||
A helper function that computes price of a VM from given cpu, ram and
|
||||
ssd, hdd and the pricing parameters
|
||||
|
||||
:param cpu: Number of cores of the VM
|
||||
:param memory: RAM of the VM
|
||||
:param ssd_size: Disk space of the VM (SSD)
|
||||
:param hdd_size: The HDD size
|
||||
:param pricing_name: The pricing name to be used
|
||||
:return: The a tuple containing the price of the VM, the VAT and the
|
||||
VAT percentage
|
||||
"""
|
||||
try:
|
||||
pricing = VMPricing.objects.get(name=pricing_name)
|
||||
except Exception as ex:
|
||||
logger.error(
|
||||
"Error getting VMPricing object for {pricing_name}."
|
||||
"Details: {details}".format(
|
||||
pricing_name=pricing_name, details=str(ex)
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
price = (
|
||||
(decimal.Decimal(cpu) * pricing.cores_unit_price) +
|
||||
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||
(decimal.Decimal(ssd_size) * pricing.ssd_unit_price) +
|
||||
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price)
|
||||
)
|
||||
if pricing.vat_inclusive:
|
||||
vat = decimal.Decimal(0)
|
||||
vat_percent = decimal.Decimal(0)
|
||||
else:
|
||||
vat = price * pricing.vat_percentage * decimal.Decimal(0.01)
|
||||
vat_percent = pricing.vat_percentage
|
||||
|
||||
cents = decimal.Decimal('.01')
|
||||
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
||||
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
|
||||
discount = {
|
||||
'name': pricing.discount_name,
|
||||
'amount': float(pricing.discount_amount),
|
||||
}
|
||||
return float(price), float(vat), float(vat_percent), discount
|
||||
|
||||
|
||||
class HostingUtils:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class BaseEmail(object):
|
|||
self.email.from_email = kwargs.get('from_address')
|
||||
else:
|
||||
self.email.from_email = '(ungleich) ungleich Support <info@ungleich.ch>'
|
||||
self.email.to = [kwargs.get('to', 'info@ungleich.com')]
|
||||
self.email.to = [kwargs.get('to', 'info@ungleich.ch')]
|
||||
|
||||
def send(self):
|
||||
self.email.send()
|
||||
|
|
|
|||
497
utils/management/commands/optimize_frontend.py
Normal file
497
utils/management/commands/optimize_frontend.py
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
"""
|
||||
This command finds and creates a report for all the usage of css rules in
|
||||
an app. It aims to optimize existing codebase as well as assist the frontend
|
||||
developer when designing new components by avoiding unnecessary duplication and
|
||||
suggesting more/optimal alternatives.
|
||||
|
||||
Features:
|
||||
Currently the command can find out and display:
|
||||
- Media Breakpoints used in a stylesheet
|
||||
- Duplicate selectors in a stylesheet
|
||||
- Unused selectors
|
||||
Work in progress to enable these features:
|
||||
- Duplicate style declaration for same selector
|
||||
- DOM validation
|
||||
- Finding out dead styles (those that are always cancelled)
|
||||
- Optimize media declarations
|
||||
|
||||
Example:
|
||||
$ python manage.py optimize_frontend datacenterlight
|
||||
above command produces a file ../optimize_frontend.html which contains a
|
||||
report with the above mentioned features
|
||||
"""
|
||||
|
||||
# import csv
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from collections import Counter, OrderedDict
|
||||
# from itertools import zip_longest
|
||||
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
RE_PATTERNS = {
|
||||
'view_html': '[\'\"](.*\.html)',
|
||||
'html_html': '{% (?:extends|include) [\'\"]?(.*\.html)',
|
||||
'html_style': '{% static [\'\"]?(.*\.css)',
|
||||
'css_media': (
|
||||
'^\s*\@media([^{]+)\{\s*([\s\S]*?})\s*}'
|
||||
),
|
||||
'css_selector': (
|
||||
'^\s*([.#\[:_A-Za-z][^{]*?)\s*'
|
||||
'\s*{\s*([\s\S]*?)\s*}'
|
||||
),
|
||||
'html_class': 'class=[\'\"]([a-zA-Z0-9-_\s]*)',
|
||||
'html_id': 'id=[\'\"]([a-zA-Z0-9-_]*)'
|
||||
}
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
'Finds unused and duplicate style declarations from the stylesheets '
|
||||
'used in the templates of each app'
|
||||
)
|
||||
requires_system_checks = False
|
||||
|
||||
def add_arguments(self, parser):
|
||||
# positional arguments
|
||||
parser.add_argument(
|
||||
'apps', nargs='+', type=str,
|
||||
help='name of the apps to be optimized'
|
||||
)
|
||||
|
||||
# Named (optional) arguments
|
||||
parser.add_argument(
|
||||
'--together',
|
||||
action='store_true',
|
||||
help='optimize the apps together'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--css',
|
||||
action='store_true',
|
||||
help='optimize only the css rules declared in each stylesheet'
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
apps_list = options['apps']
|
||||
report = {}
|
||||
for app in apps_list:
|
||||
if options['css']:
|
||||
report[app] = self.optimize_css(app)
|
||||
# write report
|
||||
write_report(report)
|
||||
|
||||
def optimize_css(self, app_name):
|
||||
"""Optimize declarations inside a css stylesheet
|
||||
|
||||
Args:
|
||||
app_name (str): The application name
|
||||
"""
|
||||
# get html and css files used in the app
|
||||
files = get_files(app_name)
|
||||
# get_selectors_from_css
|
||||
css_selectors = get_selectors_css(files['style'])
|
||||
# get_selectors_from_html
|
||||
html_selectors = get_selectors_html(files['html'])
|
||||
report = {
|
||||
'css_dup': get_css_duplication(css_selectors),
|
||||
'css_unused': get_css_unused(css_selectors, html_selectors)
|
||||
}
|
||||
return report
|
||||
|
||||
|
||||
def get_files(app_name):
|
||||
"""Get all the `html` and `css` files used in an app.
|
||||
|
||||
Args:
|
||||
app_name (str): The application name
|
||||
|
||||
Returns:
|
||||
dict: A dictonary containing Counter of occurence of each
|
||||
html and css file in `html` and `style` fields respectively.
|
||||
For example:
|
||||
{
|
||||
'html': {'datacenterlight/success.html': 1},
|
||||
'style': {'datacenterlight/css/bootstrap.min.css': 2}
|
||||
}
|
||||
"""
|
||||
# the view file for the app
|
||||
app_view = os.path.join(settings.PROJECT_DIR, app_name, 'views.py')
|
||||
# get template files called from the view
|
||||
all_html_list = file_match_pattern(app_view, 'view_html')
|
||||
# list of unique template files
|
||||
uniq_html_list = list(OrderedDict.fromkeys(all_html_list).keys())
|
||||
# list of stylesheets
|
||||
all_style_list = []
|
||||
file_patterns = ['html_html', 'html_style']
|
||||
# get html and css files called from within templates
|
||||
i = 0
|
||||
while i < len(uniq_html_list):
|
||||
template_name = uniq_html_list[i]
|
||||
try:
|
||||
temp_files = templates_match_pattern(
|
||||
template_name, file_patterns
|
||||
)
|
||||
except template.exceptions.TemplateDoesNotExist as e:
|
||||
print("template file not found: ", str(e))
|
||||
all_html_list = [
|
||||
h for h in all_html_list if h != template_name
|
||||
]
|
||||
del uniq_html_list[i]
|
||||
else:
|
||||
all_html_list.extend(temp_files[0])
|
||||
uniq_html_list = list(
|
||||
OrderedDict.fromkeys(all_html_list).keys()
|
||||
)
|
||||
all_style_list.extend(temp_files[1])
|
||||
i += 1
|
||||
# counter dict for the html files called from view
|
||||
result = {
|
||||
'html': Counter(all_html_list),
|
||||
'style': Counter(all_style_list)
|
||||
}
|
||||
# print(result)
|
||||
return result
|
||||
|
||||
|
||||
def get_selectors_css(files):
|
||||
"""Gets the selectors and declarations from a stylesheet.
|
||||
|
||||
Args:
|
||||
files (list): A list of path of stylesheets.
|
||||
|
||||
Returns:
|
||||
dict: A nested dictionary with the structre as
|
||||
`{'file': {'media-selector': [('selectors',`declarations')]}}`
|
||||
For example:
|
||||
{
|
||||
'datacenterlight/css/landing-page.css':{
|
||||
'(min-width: 768px)': [
|
||||
('.lead-right', 'text-align: right;'),
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
selectors = {}
|
||||
media_selectors = {}
|
||||
# get media selectors and other simple declarations
|
||||
for file in files:
|
||||
if any(vendor in file for vendor in ['bootstrap', 'font-awesome']):
|
||||
continue
|
||||
result = finders.find(file)
|
||||
if result:
|
||||
with open(result) as f:
|
||||
data = f.read()
|
||||
media_selectors[file] = string_match_pattern(data, 'css_media')
|
||||
new_data = string_remove_pattern(data, 'css_media')
|
||||
default_match = string_match_pattern(new_data, 'css_selector')
|
||||
selectors[file] = {
|
||||
'default': [
|
||||
[' '.join(grp.split()) for grp in m] for m in default_match
|
||||
]
|
||||
}
|
||||
# get declarations from media queries
|
||||
for file, match_list in media_selectors.items():
|
||||
for match in match_list:
|
||||
query = match[0]
|
||||
block_text = ' '.join(match[1].split())
|
||||
results = string_match_pattern(
|
||||
block_text, 'css_selector'
|
||||
)
|
||||
f_query = ' '.join(query.replace(':', ': ').split())
|
||||
if f_query in selectors[file]:
|
||||
selectors[file][f_query].extend(results)
|
||||
else:
|
||||
selectors[file][f_query] = results
|
||||
return selectors
|
||||
|
||||
|
||||
def get_selectors_html(files):
|
||||
"""Get `class` and `id` used in html files.
|
||||
|
||||
Args:
|
||||
files (list): A list of html files path.
|
||||
|
||||
Returns:
|
||||
dict: a dictonary of all the classes and ids found in the file, in
|
||||
`class` and `id` field respectively.
|
||||
"""
|
||||
selectors = {}
|
||||
for file in files:
|
||||
results = templates_match_pattern(file, ['html_class', 'html_id'])
|
||||
class_dict = {c: 1 for match in results[0] for c in match.split()}
|
||||
selectors[file] = {
|
||||
'classes': list(class_dict.keys()),
|
||||
'ids': results[1],
|
||||
}
|
||||
return selectors
|
||||
|
||||
|
||||
def file_match_pattern(file, patterns):
|
||||
"""Match a regex pattern in a file
|
||||
|
||||
Args:
|
||||
file (str): Complete path of file
|
||||
patterns (list or str): The pattern(s) to be searched in the file
|
||||
|
||||
Returns:
|
||||
list: A list of all the matches in the file. Each item is a list of
|
||||
all the captured groups in the pattern. If multiple patterns are given,
|
||||
the returned list is a list of such lists.
|
||||
For example:
|
||||
[('.lead', 'font-size: 18px;'), ('.btn-lg', 'min-width: 180px;')]
|
||||
"""
|
||||
with open(file) as f:
|
||||
data = f.read()
|
||||
results = string_match_pattern(data, patterns)
|
||||
return results
|
||||
|
||||
|
||||
def string_match_pattern(data, patterns):
|
||||
"""Match a regex pattern in a string
|
||||
|
||||
Args:
|
||||
data (str): the string to search for the pattern
|
||||
patterns (list or str): The pattern(s) to be searched in the file
|
||||
|
||||
Returns:
|
||||
list: A list of all the matches in the string. Each item is a list of
|
||||
all the captured groups in the pattern. If multiple patterns are given,
|
||||
the returned list is a list of such lists.
|
||||
For example:
|
||||
[('.lead', 'font-size: 18px;'), ('.btn-lg', 'min-width: 180px;')]
|
||||
"""
|
||||
if not isinstance(patterns, str):
|
||||
results = []
|
||||
for p in patterns:
|
||||
re_pattern = re.compile(RE_PATTERNS[p], re.MULTILINE)
|
||||
results.append(re.findall(re_pattern, data))
|
||||
else:
|
||||
re_pattern = re.compile(RE_PATTERNS[patterns], re.MULTILINE)
|
||||
results = re.findall(re_pattern, data)
|
||||
return results
|
||||
|
||||
|
||||
def string_remove_pattern(data, patterns):
|
||||
"""Remove a pattern from a string
|
||||
|
||||
Args:
|
||||
data (str): the string to search for the patter
|
||||
patterns (list or str): The pattern(s) to be removed from the file
|
||||
|
||||
Returns:
|
||||
str: The new string with all instance of matching pattern
|
||||
removed from it
|
||||
"""
|
||||
if not isinstance(patterns, str):
|
||||
for p in patterns:
|
||||
re_pattern = re.compile(RE_PATTERNS[p], re.MULTILINE)
|
||||
data = re.sub(re_pattern, '', data)
|
||||
else:
|
||||
re_pattern = re.compile(RE_PATTERNS[patterns], re.MULTILINE)
|
||||
data = re.sub(re_pattern, '', data)
|
||||
return data
|
||||
|
||||
|
||||
def templates_match_pattern(template_name, patterns):
|
||||
"""Match a regex pattern in the first found template file
|
||||
|
||||
Args:
|
||||
file (str): Path of template file
|
||||
patterns (list or str): The pattern(s) to be searched in the file
|
||||
|
||||
Returns:
|
||||
list: A list of all the matches in the file. Each item is a list of
|
||||
all the captured groups in the pattern. If multiple patterns are given,
|
||||
the returned list is a list of such lists.
|
||||
For example:
|
||||
[('.lead', 'font-size: 18px;'), ('.btn-lg', 'min-width: 180px;')]
|
||||
"""
|
||||
t = template.loader.get_template(template_name)
|
||||
data = t.template.source
|
||||
results = string_match_pattern(data, patterns)
|
||||
return results
|
||||
|
||||
|
||||
def get_css_duplication(css_selectors):
|
||||
"""Get duplicate selectors from the same stylesheet
|
||||
|
||||
Args:
|
||||
css_selectors (dict): A dictonary containing css selectors from
|
||||
all the files in the app in the below structure.
|
||||
`{'file': {'media-selector': [('selectors',`declarations')]}}`
|
||||
|
||||
Returns:
|
||||
dict: A dictonary containing the count of any duplicate selector in
|
||||
each file.
|
||||
`{'file': {'media-selector': {'selector': count}}}`
|
||||
"""
|
||||
# duplicate css selectors in stylesheets
|
||||
rule_count = {}
|
||||
for file, media_selectors in css_selectors.items():
|
||||
rule_count[file] = {}
|
||||
for media, rules in media_selectors.items():
|
||||
rules_dict = Counter([rule[0] for rule in rules])
|
||||
dup_rules_dict = {k: v for k, v in rules_dict.items() if v > 1}
|
||||
if dup_rules_dict:
|
||||
rule_count[file][media] = dup_rules_dict
|
||||
return rule_count
|
||||
|
||||
|
||||
def get_css_unused(css_selectors, html_selectors):
|
||||
"""Get selectors from stylesheets that are not used in any of the html
|
||||
files in which the stylesheet is used.
|
||||
|
||||
Args:
|
||||
css_selectors (dict): A dictonary containing css selectors from
|
||||
all the files in the app in the below structure.
|
||||
`{'file': {'media-selector': [('selectors',`declarations')]}}`
|
||||
html_selectors (dict): A dictonary containing the 'class' and 'id'
|
||||
declarations from all html files
|
||||
"""
|
||||
with open('utils/optimize/test.json', 'w') as f:
|
||||
json.dump([html_selectors, css_selectors], f, indent=4)
|
||||
# print(html_selectors, css_selectors)
|
||||
|
||||
|
||||
def write_report(all_reports, filename='frontend'):
|
||||
"""Write the generated report to a file for re-use
|
||||
|
||||
Args;
|
||||
all_reports (dict): A dictonary of report obtained from different tests
|
||||
filename (str): An optional suffix for the output file
|
||||
"""
|
||||
# full_filename = 'utils/optimize/optimize_' + filename + '.html'
|
||||
# output_file = os.path.join(
|
||||
# settings.PROJECT_DIR, full_filename
|
||||
# )
|
||||
with open('utils/optimize/op_frontend.json', 'w') as f:
|
||||
json.dump(all_reports, f, indent=4)
|
||||
# with open(output_file, 'w', newline='') as f:
|
||||
# f.write(
|
||||
# template.loader.render_to_string(
|
||||
# 'utils/report.html', {'all_reports': all_reports}
|
||||
# )
|
||||
# )
|
||||
# w = csv.writer(f)
|
||||
# print(zip_longest(*results))
|
||||
# for r in zip_longest(*results):
|
||||
# w.writerow(r)
|
||||
|
||||
|
||||
# a list of all the html tags (to be moved in a json file)
|
||||
html_tags = [
|
||||
"a",
|
||||
"abbr",
|
||||
"address",
|
||||
"article",
|
||||
"area",
|
||||
"aside",
|
||||
"audio",
|
||||
"b",
|
||||
"base",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"blockquote",
|
||||
"body",
|
||||
"br",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"cite",
|
||||
"code",
|
||||
"col",
|
||||
"colgroup",
|
||||
"datalist",
|
||||
"dd",
|
||||
"del",
|
||||
"details",
|
||||
"dfn",
|
||||
"div",
|
||||
"dl",
|
||||
"dt",
|
||||
"em",
|
||||
"embed",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"footer",
|
||||
"form",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"hgroup",
|
||||
"hr",
|
||||
"html",
|
||||
"i",
|
||||
"iframe",
|
||||
"img",
|
||||
"input",
|
||||
"ins",
|
||||
"kbd",
|
||||
"keygen",
|
||||
"label",
|
||||
"legend",
|
||||
"li",
|
||||
"link",
|
||||
"map",
|
||||
"mark",
|
||||
"menu",
|
||||
"meta",
|
||||
"meter",
|
||||
"nav",
|
||||
"noscript",
|
||||
"object",
|
||||
"ol",
|
||||
"optgroup",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"param",
|
||||
"pre",
|
||||
"progress",
|
||||
"q",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"script",
|
||||
"section",
|
||||
"select",
|
||||
"source",
|
||||
"small",
|
||||
"span",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"textarea",
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"tfoot",
|
||||
"thead",
|
||||
"th",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"u",
|
||||
"ul",
|
||||
"var",
|
||||
"video",
|
||||
"wbr"
|
||||
]
|
||||
0
utils/optimize/.gitkeep
Normal file
0
utils/optimize/.gitkeep
Normal file
|
|
@ -237,12 +237,14 @@ class StripeUtils(object):
|
|||
return return_value
|
||||
|
||||
@handleStripeError
|
||||
def subscribe_customer_to_plan(self, customer, plans):
|
||||
def subscribe_customer_to_plan(self, customer, plans, trial_end=None):
|
||||
"""
|
||||
Subscribes the given customer to the list of given plans
|
||||
|
||||
:param customer: The stripe customer identifier
|
||||
:param plans: A list of stripe plans.
|
||||
:param trial_end: An integer representing when the Stripe subscription
|
||||
is supposed to end
|
||||
Ref: https://stripe.com/docs/api/python#create_subscription-items
|
||||
e.g.
|
||||
plans = [
|
||||
|
|
@ -254,11 +256,21 @@ class StripeUtils(object):
|
|||
"""
|
||||
|
||||
subscription_result = self.stripe.Subscription.create(
|
||||
customer=customer,
|
||||
items=plans,
|
||||
customer=customer, items=plans, trial_end=trial_end
|
||||
)
|
||||
return subscription_result
|
||||
|
||||
@handleStripeError
|
||||
def unsubscribe_customer(self, subscription_id):
|
||||
"""
|
||||
Cancels a given subscription
|
||||
|
||||
:param subscription_id: The Stripe subscription id string
|
||||
:return:
|
||||
"""
|
||||
sub = stripe.Subscription.retrieve(subscription_id)
|
||||
return sub.delete()
|
||||
|
||||
@handleStripeError
|
||||
def make_payment(self, customer, amount, token):
|
||||
charge = self.stripe.Charge.create(
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class BaseTestCase(TestCase):
|
|||
|
||||
# Request Object
|
||||
self.request = HttpRequest()
|
||||
self.request.META['SERVER_NAME'] = 'ungleich.com'
|
||||
self.request.META['SERVER_NAME'] = 'ungleich.ch'
|
||||
self.request.META['SERVER_PORT'] = '80'
|
||||
|
||||
def get_client(self, user):
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from django.utils.encoding import force_bytes
|
|||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import FormView, CreateView
|
||||
from django.views.decorators.cache import cache_control
|
||||
|
||||
from membership.models import CustomUser
|
||||
from .forms import SetPasswordForm
|
||||
|
|
@ -57,6 +58,7 @@ class LoginViewMixin(FormView):
|
|||
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||
def get(self, request, *args, **kwargs):
|
||||
if self.request.user.is_authenticated():
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue