mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
boulder-oracle-sprint: Merged to [5134]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5135 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5a7802586d
commit
90740c93e6
2
AUTHORS
2
AUTHORS
@ -181,6 +181,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Luke Plant <http://lukeplant.me.uk/>
|
Luke Plant <http://lukeplant.me.uk/>
|
||||||
plisk
|
plisk
|
||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
|
polpak@yahoo.com
|
||||||
J. Rademaker
|
J. Rademaker
|
||||||
Michael Radziej <mir@noris.de>
|
Michael Radziej <mir@noris.de>
|
||||||
ramiro
|
ramiro
|
||||||
@ -224,6 +225,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
wam-djangobug@wamber.net
|
wam-djangobug@wamber.net
|
||||||
Dan Watson <http://theidioteque.net/>
|
Dan Watson <http://theidioteque.net/>
|
||||||
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
||||||
|
charly.wilhelm@gmail.com
|
||||||
Rachel Willmer <http://www.willmer.com/kb/>
|
Rachel Willmer <http://www.willmer.com/kb/>
|
||||||
Gary Wilson <gary.wilson@gmail.com>
|
Gary Wilson <gary.wilson@gmail.com>
|
||||||
wojtek
|
wojtek
|
||||||
|
0
django/contrib/localflavor/ch/__init__.py
Normal file
0
django/contrib/localflavor/ch/__init__.py
Normal file
31
django/contrib/localflavor/ch/ch_states.py
Normal file
31
django/contrib/localflavor/ch/ch_states.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
STATE_CHOICES = (
|
||||||
|
('AG', _('Aargau')),
|
||||||
|
('AI', _('Appenzell Innerrhoden')),
|
||||||
|
('AR', _('Appenzell Ausserrhoden')),
|
||||||
|
('BS', _('Basel-Stadt')),
|
||||||
|
('BL', _('Basel-Land')),
|
||||||
|
('BE', _('Berne')),
|
||||||
|
('FR', _('Fribourg')),
|
||||||
|
('GE', _('Geneva')),
|
||||||
|
('GL', _('Glarus')),
|
||||||
|
('GR', _('Graubuenden')),
|
||||||
|
('JU', _('Jura')),
|
||||||
|
('LU', _('Lucerne')),
|
||||||
|
('NE', _('Neuchatel')),
|
||||||
|
('NW', _('Nidwalden')),
|
||||||
|
('OW', _('Obwalden')),
|
||||||
|
('SH', _('Schaffhausen')),
|
||||||
|
('SZ', _('Schwyz')),
|
||||||
|
('SO', _('Solothurn')),
|
||||||
|
('SG', _('St. Gallen')),
|
||||||
|
('TG', _('Thurgau')),
|
||||||
|
('TI', _('Ticino')),
|
||||||
|
('UR', _('Uri')),
|
||||||
|
('VS', _('Valais')),
|
||||||
|
('VD', _('Vaud')),
|
||||||
|
('ZG', _('Zug')),
|
||||||
|
('ZH', _('Zurich'))
|
||||||
|
)
|
109
django/contrib/localflavor/ch/forms.py
Normal file
109
django/contrib/localflavor/ch/forms.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
Swiss-specific Form helpers
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.newforms import ValidationError
|
||||||
|
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
|
||||||
|
from django.utils.encoding import smart_unicode
|
||||||
|
from django.utils.translation import gettext
|
||||||
|
import re
|
||||||
|
|
||||||
|
id_re = re.compile(r"^(?P<idnumber>\w{8})(?P<pos9>(\d{1}|<))(?P<checksum>\d{1})$")
|
||||||
|
phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
|
||||||
|
|
||||||
|
class CHZipCodeField(RegexField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CHZipCodeField, self).__init__(r'^\d{4}$',
|
||||||
|
max_length=None, min_length=None,
|
||||||
|
error_message=gettext('Enter a zip code in the format XXXX.'),
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
class CHPhoneNumberField(Field):
|
||||||
|
"""
|
||||||
|
Validate local Swiss phone number (not international ones)
|
||||||
|
The correct format is '0XX XXX XX XX'.
|
||||||
|
'0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
|
||||||
|
'0XX XXX XX XX'.
|
||||||
|
"""
|
||||||
|
def clean(self, value):
|
||||||
|
super(CHPhoneNumberField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
value = re.sub('(\.|\s|/|-)', '', smart_unicode(value))
|
||||||
|
m = phone_digits_re.search(value)
|
||||||
|
if m:
|
||||||
|
return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
|
||||||
|
raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.')
|
||||||
|
|
||||||
|
class CHStateSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select widget that uses a list of CH states as its choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from ch_states import STATE_CHOICES # relative import
|
||||||
|
super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
||||||
|
|
||||||
|
class CHIdentityCardNumberField(Field):
|
||||||
|
"""
|
||||||
|
A Swiss identity card number.
|
||||||
|
|
||||||
|
Checks the following rules to determine whether the number is valid:
|
||||||
|
|
||||||
|
* Conforms to the X1234567<0 or 1234567890 format.
|
||||||
|
* Included checksums match calculated checksums
|
||||||
|
|
||||||
|
Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
|
||||||
|
"""
|
||||||
|
def has_valid_checksum(self, number):
|
||||||
|
given_number, given_checksum = number[:-1], number[-1]
|
||||||
|
new_number = given_number
|
||||||
|
calculated_checksum = 0
|
||||||
|
fragment = ""
|
||||||
|
parameter = 7
|
||||||
|
|
||||||
|
first = str(number[:1])
|
||||||
|
if first.isalpha():
|
||||||
|
num = ord(first.upper()) - 65
|
||||||
|
if num < 0 or num > 8:
|
||||||
|
return False
|
||||||
|
new_number = str(num) + new_number[1:]
|
||||||
|
new_number = new_number[:8] + '0'
|
||||||
|
|
||||||
|
if not new_number.isdigit():
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(new_number)):
|
||||||
|
fragment = int(new_number[i])*parameter
|
||||||
|
calculated_checksum += fragment
|
||||||
|
|
||||||
|
if parameter == 1:
|
||||||
|
parameter = 7
|
||||||
|
elif parameter == 3:
|
||||||
|
parameter = 1
|
||||||
|
elif parameter ==7:
|
||||||
|
parameter = 3
|
||||||
|
|
||||||
|
return str(calculated_checksum)[-1] == given_checksum
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
super(CHIdentityCardNumberField, self).clean(value)
|
||||||
|
error_msg = gettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.')
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
match = re.match(id_re, value)
|
||||||
|
if not match:
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
|
||||||
|
|
||||||
|
if idnumber == '00000000' or \
|
||||||
|
idnumber == 'A0000000':
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
all_digits = "%s%s%s" % (idnumber, pos9, checksum)
|
||||||
|
if not self.has_valid_checksum(all_digits):
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
return u'%s%s%s' % (idnumber, pos9, checksum)
|
||||||
|
|
@ -598,6 +598,7 @@ def syncdb(verbosity=1, interactive=True):
|
|||||||
# Install custom SQL for the app (but only if this
|
# Install custom SQL for the app (but only if this
|
||||||
# is a model we've just created)
|
# is a model we've just created)
|
||||||
for app in models.get_apps():
|
for app in models.get_apps():
|
||||||
|
app_name = app.__name__.split('.')[-2]
|
||||||
for model in models.get_models(app):
|
for model in models.get_models(app):
|
||||||
if model in created_models:
|
if model in created_models:
|
||||||
custom_sql = get_custom_sql_for_model(model)
|
custom_sql = get_custom_sql_for_model(model)
|
||||||
|
@ -893,6 +893,12 @@ class USStateField(Field):
|
|||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.USStateField]
|
return [oldforms.USStateField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
from django.contrib.localflavor.us.forms import USStateSelect
|
||||||
|
defaults = {'widget': USStateSelect}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(USStateField, self).formfield(**defaults)
|
||||||
|
|
||||||
class XMLField(TextField):
|
class XMLField(TextField):
|
||||||
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
||||||
self.schema_path = schema_path
|
self.schema_path = schema_path
|
||||||
|
@ -903,6 +903,13 @@ def find_field(name, field_list, related_query):
|
|||||||
return None
|
return None
|
||||||
return matches[0]
|
return matches[0]
|
||||||
|
|
||||||
|
def field_choices(field_list, related_query):
|
||||||
|
if related_query:
|
||||||
|
choices = [f.field.related_query_name() for f in field_list]
|
||||||
|
else:
|
||||||
|
choices = [f.name for f in field_list]
|
||||||
|
return choices
|
||||||
|
|
||||||
def lookup_inner(path, lookup_type, value, opts, table, column):
|
def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||||
qn = backend.quote_name
|
qn = backend.quote_name
|
||||||
joins, where, params = SortedDict(), [], []
|
joins, where, params = SortedDict(), [], []
|
||||||
@ -987,7 +994,11 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
|||||||
except FieldFound: # Match found, loop has been shortcut.
|
except FieldFound: # Match found, loop has been shortcut.
|
||||||
pass
|
pass
|
||||||
else: # No match found.
|
else: # No match found.
|
||||||
raise TypeError, "Cannot resolve keyword '%s' into field" % name
|
choices = field_choices(current_opts.many_to_many, False) + \
|
||||||
|
field_choices(current_opts.get_all_related_many_to_many_objects(), True) + \
|
||||||
|
field_choices(current_opts.get_all_related_objects(), True) + \
|
||||||
|
field_choices(current_opts.fields, False)
|
||||||
|
raise TypeError, "Cannot resolve keyword '%s' into field, choices are: %s" % (name, ", ".join(choices))
|
||||||
|
|
||||||
# Check whether an intermediate join is required between current_table
|
# Check whether an intermediate join is required between current_table
|
||||||
# and new_table.
|
# and new_table.
|
||||||
|
@ -500,44 +500,17 @@ in standard language format. For example, U.S. English is ``"en-us"``. See the
|
|||||||
LANGUAGES
|
LANGUAGES
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Default: A tuple of all available languages. Currently, this is::
|
Default: A tuple of all available languages. This list is continually growing
|
||||||
|
and including a copy here would inevitably become rapidly out of date. You can
|
||||||
|
see the current list of translated languages by looking in
|
||||||
|
``django/conf/global_settings.py`` (or view the `online source`_).
|
||||||
|
|
||||||
LANGUAGES = (
|
.. _online source: http://code.djangoproject.com/browser/django/trunk/django/conf/global_settings.py
|
||||||
('ar', _('Arabic')),
|
|
||||||
('bn', _('Bengali')),
|
|
||||||
('cs', _('Czech')),
|
|
||||||
('cy', _('Welsh')),
|
|
||||||
('da', _('Danish')),
|
|
||||||
('de', _('German')),
|
|
||||||
('el', _('Greek')),
|
|
||||||
('en', _('English')),
|
|
||||||
('es', _('Spanish')),
|
|
||||||
('es_AR', _('Argentinean Spanish')),
|
|
||||||
('fr', _('French')),
|
|
||||||
('gl', _('Galician')),
|
|
||||||
('hu', _('Hungarian')),
|
|
||||||
('he', _('Hebrew')),
|
|
||||||
('is', _('Icelandic')),
|
|
||||||
('it', _('Italian')),
|
|
||||||
('ja', _('Japanese')),
|
|
||||||
('nl', _('Dutch')),
|
|
||||||
('no', _('Norwegian')),
|
|
||||||
('pt-br', _('Brazilian')),
|
|
||||||
('ro', _('Romanian')),
|
|
||||||
('ru', _('Russian')),
|
|
||||||
('sk', _('Slovak')),
|
|
||||||
('sl', _('Slovenian')),
|
|
||||||
('sr', _('Serbian')),
|
|
||||||
('sv', _('Swedish')),
|
|
||||||
('ta', _('Tamil')),
|
|
||||||
('uk', _('Ukrainian')),
|
|
||||||
('zh-cn', _('Simplified Chinese')),
|
|
||||||
('zh-tw', _('Traditional Chinese')),
|
|
||||||
)
|
|
||||||
|
|
||||||
A tuple of two-tuples in the format (language code, language name). This
|
The list is a tuple of two-tuples in the format (language code, language
|
||||||
specifies which languages are available for language selection. See the
|
name) -- for example, ``('ja', 'Japanese')``. This specifies which languages
|
||||||
`internationalization docs`_ for details.
|
are available for language selection. See the `internationalization docs`_ for
|
||||||
|
details.
|
||||||
|
|
||||||
Generally, the default value should suffice. Only set this setting if you want
|
Generally, the default value should suffice. Only set this setting if you want
|
||||||
to restrict language selection to a subset of the Django-provided languages.
|
to restrict language selection to a subset of the Django-provided languages.
|
||||||
|
@ -71,7 +71,7 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Author.objects.filter(firstname__exact='John')
|
>>> Author.objects.filter(firstname__exact='John')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'firstname' into field
|
TypeError: Cannot resolve keyword 'firstname' into field, choices are: article, id, first_name, last_name
|
||||||
|
|
||||||
>>> a = Author.objects.get(last_name__exact='Smith')
|
>>> a = Author.objects.get(last_name__exact='Smith')
|
||||||
>>> a.first_name
|
>>> a.first_name
|
||||||
|
@ -223,11 +223,11 @@ DoesNotExist: Article matching query does not exist.
|
|||||||
>>> Article.objects.filter(pub_date_year='2005').count()
|
>>> Article.objects.filter(pub_date_year='2005').count()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'pub_date_year' into field
|
TypeError: Cannot resolve keyword 'pub_date_year' into field, choices are: id, headline, pub_date
|
||||||
|
|
||||||
>>> Article.objects.filter(headline__starts='Article')
|
>>> Article.objects.filter(headline__starts='Article')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'headline__starts' into field
|
TypeError: Cannot resolve keyword 'headline__starts' into field, choices are: id, headline, pub_date
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -174,13 +174,13 @@ False
|
|||||||
>>> Article.objects.filter(reporter_id__exact=1)
|
>>> Article.objects.filter(reporter_id__exact=1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'reporter_id' into field
|
TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter
|
||||||
|
|
||||||
# You need to specify a comparison clause
|
# You need to specify a comparison clause
|
||||||
>>> Article.objects.filter(reporter_id=1)
|
>>> Article.objects.filter(reporter_id=1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'reporter_id' into field
|
TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter
|
||||||
|
|
||||||
# You can also instantiate an Article by passing
|
# You can also instantiate an Article by passing
|
||||||
# the Reporter's ID instead of a Reporter object.
|
# the Reporter's ID instead of a Reporter object.
|
||||||
|
@ -55,5 +55,5 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Poll.objects.get(choice__name__exact="This is the answer")
|
>>> Poll.objects.get(choice__name__exact="This is the answer")
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'choice' into field
|
TypeError: Cannot resolve keyword 'choice' into field, choices are: poll_choice, related_choice, id, question, creator
|
||||||
"""}
|
"""}
|
||||||
|
@ -1011,6 +1011,60 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.']
|
ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.']
|
||||||
|
|
||||||
|
# CHZipCodeField ############################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHZipCodeField
|
||||||
|
>>> f = CHZipCodeField()
|
||||||
|
>>> f.clean('800x')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a zip code in the format XXXX.']
|
||||||
|
>>> f.clean('80 00')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a zip code in the format XXXX.']
|
||||||
|
>>> f.clean('8000')
|
||||||
|
u'8000'
|
||||||
|
|
||||||
|
# CHPhoneNumberField ########################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHPhoneNumberField
|
||||||
|
>>> f = CHPhoneNumberField()
|
||||||
|
>>> f.clean('01234567890')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.']
|
||||||
|
>>> f.clean('1234567890')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.']
|
||||||
|
>>> f.clean('0123456789')
|
||||||
|
u'012 345 67 89'
|
||||||
|
|
||||||
|
# CHIdentityCardNumberField #################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHIdentityCardNumberField
|
||||||
|
>>> f = CHIdentityCardNumberField()
|
||||||
|
>>> f.clean('C1234567<0')
|
||||||
|
u'C1234567<0'
|
||||||
|
>>> f.clean('C1234567<1')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.']
|
||||||
|
>>> f.clean('2123456700')
|
||||||
|
u'2123456700'
|
||||||
|
>>> f.clean('2123456701')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.']
|
||||||
|
|
||||||
|
# CHStateSelect #############################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHStateSelect
|
||||||
|
>>> w = CHStateSelect()
|
||||||
|
>>> w.render('state', 'AG')
|
||||||
|
u'<select name="state">\n<option value="AG" selected="selected">Aargau</option>\n<option value="AI">Appenzell Innerrhoden</option>\n<option value="AR">Appenzell Ausserrhoden</option>\n<option value="BS">Basel-Stadt</option>\n<option value="BL">Basel-Land</option>\n<option value="BE">Berne</option>\n<option value="FR">Fribourg</option>\n<option value="GE">Geneva</option>\n<option value="GL">Glarus</option>\n<option value="GR">Graubuenden</option>\n<option value="JU">Jura</option>\n<option value="LU">Lucerne</option>\n<option value="NE">Neuchatel</option>\n<option value="NW">Nidwalden</option>\n<option value="OW">Obwalden</option>\n<option value="SH">Schaffhausen</option>\n<option value="SZ">Schwyz</option>\n<option value="SO">Solothurn</option>\n<option value="SG">St. Gallen</option>\n<option value="TG">Thurgau</option>\n<option value="TI">Ticino</option>\n<option value="UR">Uri</option>\n<option value="VS">Valais</option>\n<option value="VD">Vaud</option>\n<option value="ZG">Zug</option>\n<option value="ZH">Zurich</option>\n</select>'
|
||||||
|
|
||||||
## AUPostCodeField ##########################################################
|
## AUPostCodeField ##########################################################
|
||||||
|
|
||||||
A field that accepts a four digit Australian post code.
|
A field that accepts a four digit Australian post code.
|
||||||
|
@ -32,7 +32,7 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Choice.objects.filter(foo__exact=None)
|
>>> Choice.objects.filter(foo__exact=None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'foo' into field
|
TypeError: Cannot resolve keyword 'foo' into field, choices are: id, poll, choice
|
||||||
|
|
||||||
# Can't use None on anything other than __exact
|
# Can't use None on anything other than __exact
|
||||||
>>> Choice.objects.filter(id__gt=None)
|
>>> Choice.objects.filter(id__gt=None)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user