diff --git a/apps/blog/admin.py b/apps/blog/admin.py index 91d1cdd..02da2fa 100644 --- a/apps/blog/admin.py +++ b/apps/blog/admin.py @@ -1,9 +1,11 @@ from django.contrib import admin from .models import Post, PostComment, PostLike, PostDislike, PostCommentLike +from unfold.admin import ModelAdmin + @admin.register(Post) -class PostAdmin(admin.ModelAdmin): +class PostAdmin(ModelAdmin): list_display = ["title", "content", "author", "is_active"] search_fields = ["title", "content"] list_filter = ["author", "is_active"] @@ -12,20 +14,20 @@ class PostAdmin(admin.ModelAdmin): @admin.register(PostComment) -class PostCommentAdmin(admin.ModelAdmin): +class PostCommentAdmin(ModelAdmin): pass @admin.register(PostLike) -class PostLikeAdmin(admin.ModelAdmin): +class PostLikeAdmin(ModelAdmin): pass @admin.register(PostDislike) -class PostDislike(admin.ModelAdmin): +class PostDislike(ModelAdmin): pass @admin.register(PostCommentLike) -class PostCommentLikeAdmin(admin.ModelAdmin): +class PostCommentLikeAdmin(ModelAdmin): pass diff --git a/apps/blog/filters.py b/apps/blog/filters.py new file mode 100644 index 0000000..fe8d9e9 --- /dev/null +++ b/apps/blog/filters.py @@ -0,0 +1,14 @@ +import django_filters + +from .models import Post + + +class PostFilter(django_filters.FilterSet): + title = django_filters.CharFilter(lookup_expr="icontains") + description = django_filters.CharFilter(lookup_expr="icontains") + content = django_filters.CharFilter(lookup_expr="icontains") + created_at = django_filters.DateFromToRangeFilter() + + class Meta: + model = Post + fields = ["title", "description", "content", "created_at"] diff --git a/apps/blog/utils.py b/apps/blog/utils.py index 40c3a10..ee169d4 100644 --- a/apps/blog/utils.py +++ b/apps/blog/utils.py @@ -1,23 +1,44 @@ from django.db.models import QuerySet from django.db.models import Q -from django.core.paginator import Paginator, Page, EmptyPage, PageNotAnInteger +from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector +from django.core.paginator import ( + Paginator, + Page, + EmptyPage, + PageNotAnInteger, + InvalidPage, +) +from django.conf import settings from .models import PostLike, PostDislike, Post, PostComment -def get_search_model_queryset( - model_queryset: QuerySet, search_query: str = None -) -> QuerySet: - if not search_query: +def get_search_model_queryset(model_queryset: QuerySet, query: str = None) -> QuerySet: + if not query: return model_queryset - search_query = model_queryset.filter( - Q(title__icontains=search_query) - | Q(description__icontains=search_query) - | Q(content__icontains=search_query) - ) - - return search_query + search_vector = SearchVector("title", "description", "content") + search_query = SearchQuery(query) + + if settings.DATABASES["default"]["ENGINE"] == "django.db.backends.postgresql": + # PostgreSQL search + queryset = ( + model_queryset.annotate( + search=search_vector, + rank=SearchRank(search_vector, search_query), + ) + .filter(search=search_query) + .order_by("-rank") + ) + else: + # SQLite3 search + queryset = model_queryset.filter( + Q(title__icontains=query) + | Q(description__icontains=query) + | Q(content__icontains=query) + ) + + return queryset def get_pagination_obj(model_queryset: QuerySet, page: int = 1, size: int = 4) -> Page: @@ -29,6 +50,9 @@ def get_pagination_obj(model_queryset: QuerySet, page: int = 1, size: int = 4) - except PageNotAnInteger: page_obj = paginator.page(1) + except InvalidPage: + page_obj = paginator.page(1) + except EmptyPage: page_obj = paginator.page(paginator.num_pages) diff --git a/apps/shared/management/commands/createadmin.py b/apps/shared/management/commands/createadmin.py index ae3f06f..dcb4660 100644 --- a/apps/shared/management/commands/createadmin.py +++ b/apps/shared/management/commands/createadmin.py @@ -4,6 +4,7 @@ from django.core.management import BaseCommand from dotenv import load_dotenv + load_dotenv() diff --git a/apps/users/admin.py b/apps/users/admin.py index 269ec5f..0a15963 100644 --- a/apps/users/admin.py +++ b/apps/users/admin.py @@ -1,11 +1,14 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin # noqa from .models import User, UserProfile +from unfold.admin import ModelAdmin + @admin.register(User) -class UserAdmin(admin.ModelAdmin): +class UserAdmin(ModelAdmin): list_display = ["username", "post_count"] - search_fields = ["first_name", "last_name", "username"] + search_fields = ["first_name", "last_name", "username", "email"] list_display_links = ["username"] def get_post_count(self): @@ -13,5 +16,5 @@ def get_post_count(self): @admin.register(UserProfile) -class UserProfileAdmin(admin.ModelAdmin): - pass +class UserProfileAdmin(ModelAdmin): + list_display = ["user", "avatar", "bio"] diff --git a/apps/users/api_endpoints/users/User/tests.py b/apps/users/api_endpoints/users/User/tests.py index d8c4c7c..d8181e2 100644 --- a/apps/users/api_endpoints/users/User/tests.py +++ b/apps/users/api_endpoints/users/User/tests.py @@ -102,3 +102,6 @@ def _create_user(): _check_user_error_field() _check_user_passwords_field() _create_user() + + def test_api_user_update(self): + pass diff --git a/apps/users/api_endpoints/users/UserProfile/tests.py b/apps/users/api_endpoints/users/UserProfile/tests.py index e69de29..935ce5e 100644 --- a/apps/users/api_endpoints/users/UserProfile/tests.py +++ b/apps/users/api_endpoints/users/UserProfile/tests.py @@ -0,0 +1,20 @@ +from rest_framework.test import APITestCase +from rest_framework_simplejwt.tokens import RefreshToken +from apps.users.models import UserProfile, User # noqa + + +class UserProfileApiTestCase(APITestCase): + def setUp(self) -> None: + self.username = "Admin" + self.email = "admin@gmail.com" + self.password = "password" + user = User.objects.create( + username=self.username, + email=self.email, + ) + user.set_password(self.password) + user.save() + self.user = user + refresh = RefreshToken.for_user(self.user) + self.token = str(refresh.access_token) + return super().setUp() diff --git a/apps/users/middleware.py b/apps/users/middleware.py index 656cb40..6d457c2 100644 --- a/apps/users/middleware.py +++ b/apps/users/middleware.py @@ -10,6 +10,7 @@ class JWTAuthMiddleware(MiddlewareMixin): def process_request(self, request): + # If admin auth for session if request.path.startswith("/admin"): return diff --git a/core/asgi.py b/core/asgi.py index 762ffa4..2a55ab5 100644 --- a/core/asgi.py +++ b/core/asgi.py @@ -1,16 +1,16 @@ -""" -ASGI config for config project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ -""" - import os from django.core.asgi import get_asgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") +from dotenv import load_dotenv + +load_dotenv() + +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", + os.getenv("DJANGO_SETTINGS_MODULE", "core.settings.development"), +) application = get_asgi_application() + +app = application diff --git a/core/config/__init__.py b/core/config/__init__.py index 6cc4c3b..cee22ae 100644 --- a/core/config/__init__.py +++ b/core/config/__init__.py @@ -1,3 +1,7 @@ from .apps import * # noqa from .jwt import * # noqa from .rest_framework import * # noqa +from .unfold_navigation import * # noqa +from .unfold import * # noqa + +# from .cheditor5 import * # noqa diff --git a/core/config/apps.py b/core/config/apps.py index 83de4a9..dc75c09 100644 --- a/core/config/apps.py +++ b/core/config/apps.py @@ -17,7 +17,25 @@ ] THIRD_PARTY_APPS = [ + # Admin panel + "unfold", + "unfold.contrib.filters", + "unfold.contrib.forms", + "unfold.contrib.import_export", + "unfold.contrib.guardian", + "unfold.contrib.simple_history", + # Translation + "modeltranslation", + # + "django_ckeditor_5", + # Translation pannel + "rosetta", + # DRF Swaggers + "drf_spectacular", + "drf_spectacular_sidecar", + # Rest Framework "rest_framework", + # Rest Framework JWT (Json web token)s "rest_framework_simplejwt", "rest_framework_simplejwt.token_blacklist", ] diff --git a/core/config/cheditor5.py b/core/config/cheditor5.py new file mode 100644 index 0000000..7360ae9 --- /dev/null +++ b/core/config/cheditor5.py @@ -0,0 +1,140 @@ +customColorPalette = [ + {"color": "hsl(4, 90%, 58%)", "label": "Red"}, + {"color": "hsl(340, 82%, 52%)", "label": "Pink"}, + {"color": "hsl(291, 64%, 42%)", "label": "Purple"}, + {"color": "hsl(262, 52%, 47%)", "label": "Deep Purple"}, + {"color": "hsl(231, 48%, 48%)", "label": "Indigo"}, + {"color": "hsl(207, 90%, 54%)", "label": "Blue"}, +] + +CKEDITOR_5_CONFIGS = { + "default": { + "toolbar": [ + "heading", + "|", + "bold", + "italic", + "link", + "bulletedList", + "numberedList", + "blockQuote", + "imageUpload", + ], + }, + "extends": { + "blockToolbar": [ + "paragraph", + "heading1", + "heading2", + "heading3", + "|", + "bulletedList", + "numberedList", + "|", + "blockQuote", + ], + "toolbar": [ + "heading", + "|", + "outdent", + "indent", + "|", + "bold", + "italic", + "link", + "underline", + "strikethrough", + "code", + "subscript", + "superscript", + "highlight", + "|", + "codeBlock", + "sourceEditing", + "insertImage", + "bulletedList", + "numberedList", + "todoList", + "|", + "blockQuote", + "imageUpload", + "|", + "fontSize", + "fontFamily", + "fontColor", + "fontBackgroundColor", + "mediaEmbed", + "removeFormat", + "insertTable", + ], + "image": { + "toolbar": [ + "imageTextAlternative", + "|", + "imageStyle:alignLeft", + "imageStyle:alignRight", + "imageStyle:alignCenter", + "imageStyle:side", + "|", + ], + "styles": [ + "full", + "side", + "alignLeft", + "alignRight", + "alignCenter", + ], + }, + "table": { + "contentToolbar": [ + "tableColumn", + "tableRow", + "mergeTableCells", + "tableProperties", + "tableCellProperties", + ], + "tableProperties": { + "borderColors": customColorPalette, + "backgroundColors": customColorPalette, + }, + "tableCellProperties": { + "borderColors": customColorPalette, + "backgroundColors": customColorPalette, + }, + }, + "heading": { + "options": [ + { + "model": "paragraph", + "title": "Paragraph", + "class": "ck-heading_paragraph", + }, + { + "model": "heading1", + "view": "h1", + "title": "Heading 1", + "class": "ck-heading_heading1", + }, + { + "model": "heading2", + "view": "h2", + "title": "Heading 2", + "class": "ck-heading_heading2", + }, + { + "model": "heading3", + "view": "h3", + "title": "Heading 3", + "class": "ck-heading_heading3", + }, + ] + }, + }, + "list": { + "properties": { + "styles": "true", + "startIndex": "true", + "reversed": "true", + } + }, +} diff --git a/core/config/jwt.py b/core/config/jwt.py index dba6029..bf6f3aa 100644 --- a/core/config/jwt.py +++ b/core/config/jwt.py @@ -16,8 +16,8 @@ SIMPLE_JWT = { - "ACCESS_TOKEN_LIFETIME": timedelta(days=7), - "REFRESH_TOKEN_LIFETIME": timedelta(days=31), + "ACCESS_TOKEN_LIFETIME": timedelta(days=1), + "REFRESH_TOKEN_LIFETIME": timedelta(days=7), "ROTATE_REFRESH_TOKENS": True, "BLACKLIST_AFTER_ROTATION": True, "UPDATE_LAST_LOGIN": False, @@ -35,8 +35,15 @@ "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule", "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",), "TOKEN_TYPE_CLAIM": "token_type", + "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser", "JTI_CLAIM": "jti", "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp", - "SLIDING_TOKEN_LIFETIME": timedelta(days=7), - "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=31), + "SLIDING_TOKEN_LIFETIME": timedelta(days=60), + "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1), + "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer", + "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer", + "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer", + "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer", + "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer", + "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer", } diff --git a/core/config/unfold.py b/core/config/unfold.py new file mode 100644 index 0000000..048dd39 --- /dev/null +++ b/core/config/unfold.py @@ -0,0 +1,67 @@ +from django.templatetags.static import static + +from . import unfold_navigation as navigation + + +UNFOLD = { + "SITE_TITLE": "Akromjon BLOG", + "SITE_HEADER": "Akromjon BLOG", + "SITE_URL": "/", + "SITE_ICON": { + "light": lambda request: static("images/django-logo.png"), + "dark": lambda request: static("images/django-logo.png"), + }, + "SITE_FAVICONS": [ + { + "rel": "icon", + "sizes": "32x32", + "type": "image/svg+xml", + "href": lambda request: static("images/django-logo.png"), + }, + ], + "SITE_SYMBOL": "speed", + "SHOW_HISTORY": True, + "SHOW_VIEW_ON_SITE": True, + "STYLES": [ + lambda request: static("css/tailwind.css"), + ], + "LOGIN": { + "image": lambda request: static("images/login.jpg"), + }, + "COLORS": { + "font": { + "subtle-light": "107 114 128", + "subtle-dark": "156 163 175", + "default-light": "75 85 99", + "default-dark": "209 213 219", + "important-light": "17 24 39", + "important-dark": "243 244 246", + }, + "primary": { + "50": "65 144 176", + "100": "65 144 176", + "200": "65 144 176", + "300": "65 144 176", + "400": "65 144 176", + "500": "65 144 176", + "600": "65 144 176", + "700": "65 144 176", + "800": "65 144 176", + "900": "65 144 176", + "950": "65 144 176", + }, + }, + "EXTENSIONS": { + "modeltranslation": { + "flags": { + "uz": "uz", + "ru": "🇷🇺", + } + }, + }, + "SIDEBAR": { + "show_search": True, + "show_all_applications": True, + "navigation": navigation.PAGES, + }, +} diff --git a/core/config/unfold_navigation.py b/core/config/unfold_navigation.py new file mode 100644 index 0000000..322b44f --- /dev/null +++ b/core/config/unfold_navigation.py @@ -0,0 +1,51 @@ +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + + +def user_has_group_or_permission(user, permission): + if user.is_superuser: + return True + + group_names = user.groups.values_list("name", flat=True) + if not group_names: + return True + + return user.groups.filter(permissions__codename=permission).exists() + + +PAGES = [ + { + "seperator": True, + "items": [ + { + "title": _("Home"), + "icon": "home", + "link": reverse_lazy("admin:index"), + }, + ], + }, + { + "seperator": True, + "title": _("Users"), + "items": [ + { + "title": _("Groups"), + "icon": "person_add", + "link": reverse_lazy("admin:auth_group_changelist"), + "permission": lambda request: user_has_group_or_permission( + request.user, + "view_group", + ), + }, + { + "title": _("Users"), + "icon": "person_add", + "link": reverse_lazy("admin:users_user_changelist"), + "permission": lambda request: user_has_group_or_permission( + request.user, + "view_user", + ), + }, + ], + }, +] diff --git a/core/settings/base.py b/core/settings/base.py index 82bd896..d37aab5 100644 --- a/core/settings/base.py +++ b/core/settings/base.py @@ -2,6 +2,8 @@ from pathlib import Path +from django.utils.translation import gettext_lazy + from core.config import * # noqa from dotenv import load_dotenv @@ -16,19 +18,23 @@ ALLOWED_HOSTS = str(os.getenv("ALLOWED_HOSTS")).split(",") +# CSRF_TRUSTED_ORIGINS = str(os.getenv("CSRF_TRUSTED_ORIGINS")).split(",") -INSTALLED_APPS = DEFAULT_APPS + PROJECT_APPS + THIRD_PARTY_APPS # NOQA +INSTALLED_APPS = THIRD_PARTY_APPS + DEFAULT_APPS + PROJECT_APPS # noqa MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.BrokenLinkEmailsMiddleware", + # "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", + "django.middleware.locale.LocaleMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - "apps.users.middleware.JWTAuthMiddleware", + "apps.users.middleware.JWTAuthMiddleware", # My Jwt Auth Middleware ] ROOT_URLCONF = "core.urls" @@ -60,6 +66,7 @@ } WSGI_APPLICATION = "core.wsgi.application" +ASGI_APPLICATION = "core.asgi.application" AUTH_PASSWORD_VALIDATORS = [ { @@ -81,16 +88,25 @@ TIME_ZONE = "Asia/Tashkent" USE_I18N = True -# USE_L10N = True USE_TZ = True +gettext = lambda s: gettext_lazy(s) # noqa + +LANGUAGES = ( + ("ru", gettext("Russia")), + ("en", gettext("English")), + ("uz", gettext("Uzbek")), +) + +LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")] + LOGIN_URL = "/users/login/" LOGIN_REDIRECT_URL = "/" STATIC_URL = "static/" -STATIC_ROOT = BASE_DIR.joinpath("staticfiles") -STATICFILES_DIRS = [BASE_DIR.joinpath("static")] +STATICFILES_DIRS = [str(BASE_DIR.joinpath("static"))] +STATIC_ROOT = str(BASE_DIR.joinpath("staticfiles")) # AUTHENTICATION_BACKENDS = ( # 'apps.users.authentication.JWTAdminAuthentication', @@ -98,7 +114,7 @@ # ) MEDIA_URL = "media/" -MEDIA_ROOT = BASE_DIR.joinpath("media/") +MEDIA_ROOT = str(BASE_DIR.joinpath("media/")) DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/core/urls.py b/core/urls.py index 2eb33a0..21147ec 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import handler400, handler403, handler404, handler500 # noqa +from django.conf.urls.i18n import i18n_patterns # noqa: F401 from django.conf.urls.static import static from django.conf import settings @@ -24,6 +25,12 @@ # URLs urlpatterns = [ path("admin/", admin.site.urls), + path("i18n", include("django.conf.urls.i18n")), + path("rosetta/", include("rosetta.urls")), +] + +# Translated urls +urlpatterns += i18n_patterns( path("", include("apps.blog.urls", namespace="blog")), path("users/", include("apps.users.urls", namespace="users")), path("robots.txt", TemplateView.as_view(template_name="bunin/robots.txt")), @@ -33,7 +40,7 @@ {"sitemaps": sitemaps}, name="django.contrib.sitemaps.views.sitemap", ), -] +) # API Endpoints urlpatterns += [ @@ -43,9 +50,8 @@ path("api/v1/blogs/", include(blog_api_router.urls)), ] -if settings.DEBUG: - urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) - urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) handler400 = "apps.shared.views.bad_request_view" # noqa diff --git a/core/wsgi.py b/core/wsgi.py index 7546823..f832f53 100644 --- a/core/wsgi.py +++ b/core/wsgi.py @@ -3,6 +3,7 @@ from django.core.wsgi import get_wsgi_application from dotenv import load_dotenv + load_dotenv() os.environ.setdefault( diff --git a/locale/ru/LC_MESSAGES/django.po b/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..8aebbcc --- /dev/null +++ b/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,1329 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-21 13:24+0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " +"(n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: .\apps\blog\models.py:14 +msgid "title" +msgstr "" + +#: .\apps\blog\models.py:15 +msgid "slug" +msgstr "" + +#: .\apps\blog\models.py:17 +msgid "status" +msgstr "" + +#: .\apps\blog\models.py:23 +msgid "description" +msgstr "" + +#: .\apps\blog\models.py:25 +msgid "content" +msgstr "" + +#: .\apps\blog\models.py:26 +msgid "publisher at" +msgstr "" + +#: .\apps\blog\models.py:27 +msgid "active" +msgstr "" + +#: .\apps\blog\models.py:29 +msgid "watching" +msgstr "" + +#: .\apps\blog\models.py:40 +msgid "Post" +msgstr "" + +#: .\apps\blog\models.py:41 +msgid "Posts" +msgstr "" + +#: .\apps\blog\models.py:94 +msgid "message" +msgstr "" + +#: .\apps\users\models.py:9 +msgid "email address" +msgstr "" + +#: .\apps\users\models.py:26 +msgid "User" +msgstr "" + +#: .\apps\users\models.py:27 +msgid "Users" +msgstr "" + +#: .\apps\users\models.py:40 +msgid "avatar" +msgstr "" + +#: .\apps\users\models.py:47 +msgid "bio" +msgstr "" + +#: .\apps\users\models.py:51 +msgid "User Profile" +msgstr "" + +#: .\apps\users\models.py:52 +msgid "User Profiles" +msgstr "" + +#: .\core\settings\base.py:92 +msgid "Russia" +msgstr "" + +#: .\core\settings\base.py:93 +msgid "English" +msgstr "" + +#: .\core\settings\base.py:94 +msgid "Uzbek" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\messages\apps.py:16 +msgid "Messages" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\sitemaps\apps.py:8 +msgid "Site Maps" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\staticfiles\apps.py:9 +msgid "Static Files" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\syndication\apps.py:7 +msgid "Syndication" +msgstr "" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: .\venv\Lib\site-packages\django\core\paginator.py:30 +msgid "…" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:70 +msgid "Enter a valid domain name." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:155 +#: .\venv\Lib\site-packages\django\forms\fields.py:768 +msgid "Enter a valid URL." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:216 +msgid "Enter a valid integer." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:227 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: .\venv\Lib\site-packages\django\core\validators.py:310 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:318 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:330 +#: .\venv\Lib\site-packages\django\core\validators.py:339 +#: .\venv\Lib\site-packages\django\core\validators.py:353 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2215 +#, python-format +msgid "Enter a valid %(protocol)s address." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:332 +msgid "IPv4" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:341 +#: .\venv\Lib\site-packages\django\utils\ipv6.py:30 +msgid "IPv6" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:355 +msgid "IPv4 or IPv6" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:396 +msgid "Enter only digits separated by commas." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:402 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:437 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:446 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:455 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:462 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:494 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:512 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:535 +#: .\venv\Lib\site-packages\django\forms\fields.py:359 +#: .\venv\Lib\site-packages\django\forms\fields.py:398 +msgid "Enter a number." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:537 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:542 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:547 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:618 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:680 +msgid "Null characters are not allowed." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\base.py:1571 +#: .\venv\Lib\site-packages\django\forms\models.py:908 +msgid "and" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\base.py:1573 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\constraints.py:22 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:134 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:135 +msgid "This field cannot be null." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:136 +msgid "This field cannot be blank." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:137 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:141 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:180 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1156 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1157 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1159 +msgid "Boolean (Either True or False)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1209 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1211 +msgid "String (unlimited)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1320 +msgid "Comma-separated integers" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1421 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1425 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1560 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1429 +msgid "Date (without time)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1556 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1564 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1569 +msgid "Date (with time)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1696 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1698 +msgid "Decimal number" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1859 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1863 +msgid "Duration" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1915 +msgid "Email address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1940 +msgid "File path" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2018 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2020 +msgid "Floating point number" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2060 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2062 +msgid "Integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2158 +msgid "Big (8 byte) integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2175 +msgid "Small integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2183 +msgid "IPv4 address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2214 +msgid "IP address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2305 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2306 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2308 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2359 +msgid "Positive big integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2374 +msgid "Positive integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2389 +msgid "Positive small integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2405 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2441 +msgid "Text" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2521 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2525 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2529 +msgid "Time" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2637 +msgid "URL" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2661 +msgid "Raw binary data" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2726 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2728 +msgid "Universally unique identifier" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\files.py:244 +msgid "File" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\files.py:420 +msgid "Image" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\json.py:24 +msgid "A JSON object" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\json.py:26 +msgid "Value must be valid JSON." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:932 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:934 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1228 +msgid "One-to-one relationship" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1285 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1287 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1335 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: .\venv\Lib\site-packages\django\forms\boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:95 +msgid "This field is required." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:308 +msgid "Enter a whole number." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:479 +#: .\venv\Lib\site-packages\django\forms\fields.py:1260 +msgid "Enter a valid date." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:502 +#: .\venv\Lib\site-packages\django\forms\fields.py:1261 +msgid "Enter a valid time." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:529 +msgid "Enter a valid date/time." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:563 +msgid "Enter a valid duration." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:564 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:633 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:634 +msgid "No file was submitted." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:635 +msgid "The submitted file is empty." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:637 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:642 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:710 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:882 +#: .\venv\Lib\site-packages\django\forms\fields.py:968 +#: .\venv\Lib\site-packages\django\forms\models.py:1592 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:970 +#: .\venv\Lib\site-packages\django\forms\fields.py:1089 +#: .\venv\Lib\site-packages\django\forms\models.py:1590 +msgid "Enter a list of values." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1090 +msgid "Enter a complete value." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1329 +msgid "Enter a valid UUID." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1359 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: .\venv\Lib\site-packages\django\forms\forms.py:94 +msgid ":" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\forms.py:230 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:484 +#: .\venv\Lib\site-packages\django\forms\formsets.py:491 +msgid "Order" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:499 +msgid "Delete" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:901 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:906 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:913 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:922 +msgid "Please correct the duplicate values below." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1359 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1450 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1594 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\utils.py:227 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:461 +msgid "Clear" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:462 +msgid "Currently" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:463 +msgid "Change" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:800 +msgid "Unknown" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:801 +msgid "Yes" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:802 +msgid "No" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:873 +msgid "yes,no,maybe" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:903 +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:920 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:922 +#, python-format +msgid "%s KB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:924 +#, python-format +msgid "%s MB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:926 +#, python-format +msgid "%s GB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:928 +#, python-format +msgid "%s TB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:930 +#, python-format +msgid "%s PB" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:74 +msgid "p.m." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:75 +msgid "a.m." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:80 +msgid "PM" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:81 +msgid "AM" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:153 +msgid "midnight" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:155 +msgid "noon" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:7 +msgid "Monday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:8 +msgid "Tuesday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:9 +msgid "Wednesday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:10 +msgid "Thursday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:11 +msgid "Friday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:12 +msgid "Saturday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:13 +msgid "Sunday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:16 +msgid "Mon" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:17 +msgid "Tue" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:18 +msgid "Wed" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:19 +msgid "Thu" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:20 +msgid "Fri" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:21 +msgid "Sat" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:22 +msgid "Sun" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:25 +msgid "January" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:26 +msgid "February" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:27 +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:28 +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:29 +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:30 +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:31 +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:32 +msgid "August" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:33 +msgid "September" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:34 +msgid "October" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:35 +msgid "November" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:36 +msgid "December" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:39 +msgid "jan" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:40 +msgid "feb" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:41 +msgid "mar" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:42 +msgid "apr" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:43 +msgid "may" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:44 +msgid "jun" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:45 +msgid "jul" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:46 +msgid "aug" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:47 +msgid "sep" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:48 +msgid "oct" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:49 +msgid "nov" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:50 +msgid "dec" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:55 +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:69 +msgctxt "alt. month" +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\text.py:75 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\text.py:286 +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +#: .\venv\Lib\site-packages\django\utils\text.py:305 +#: .\venv\Lib\site-packages\django\utils\timesince.py:135 +msgid ", " +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:44 +msgid "No year specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:64 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:115 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:214 +msgid "Date out of range" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:94 +msgid "No month specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:147 +msgid "No day specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:194 +msgid "No week specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:349 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:380 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:652 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:692 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:169 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:68 +#: .\venv\Lib\site-packages\django\views\templates\directory_index.html:8 +#: .\venv\Lib\site-packages\django\views\templates\directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:7 +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:204 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:208 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:217 +msgid "Django Documentation" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:218 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:226 +msgid "Tutorial: A Polling App" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:227 +msgid "Get started with Django" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:235 +msgid "Django Community" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:236 +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/locale/uz/LC_MESSAGES/django.po b/locale/uz/LC_MESSAGES/django.po new file mode 100644 index 0000000..94afbbe --- /dev/null +++ b/locale/uz/LC_MESSAGES/django.po @@ -0,0 +1,1327 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-21 13:24+0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: .\apps\blog\models.py:14 +msgid "title" +msgstr "" + +#: .\apps\blog\models.py:15 +msgid "slug" +msgstr "" + +#: .\apps\blog\models.py:17 +msgid "status" +msgstr "" + +#: .\apps\blog\models.py:23 +msgid "description" +msgstr "" + +#: .\apps\blog\models.py:25 +msgid "content" +msgstr "" + +#: .\apps\blog\models.py:26 +msgid "publisher at" +msgstr "" + +#: .\apps\blog\models.py:27 +msgid "active" +msgstr "" + +#: .\apps\blog\models.py:29 +msgid "watching" +msgstr "" + +#: .\apps\blog\models.py:40 +msgid "Post" +msgstr "" + +#: .\apps\blog\models.py:41 +msgid "Posts" +msgstr "" + +#: .\apps\blog\models.py:94 +msgid "message" +msgstr "" + +#: .\apps\users\models.py:9 +msgid "email address" +msgstr "" + +#: .\apps\users\models.py:26 +msgid "User" +msgstr "" + +#: .\apps\users\models.py:27 +msgid "Users" +msgstr "" + +#: .\apps\users\models.py:40 +msgid "avatar" +msgstr "" + +#: .\apps\users\models.py:47 +msgid "bio" +msgstr "" + +#: .\apps\users\models.py:51 +msgid "User Profile" +msgstr "" + +#: .\apps\users\models.py:52 +msgid "User Profiles" +msgstr "" + +#: .\core\settings\base.py:92 +msgid "Russia" +msgstr "" + +#: .\core\settings\base.py:93 +msgid "English" +msgstr "" + +#: .\core\settings\base.py:94 +msgid "Uzbek" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\messages\apps.py:16 +msgid "Messages" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\sitemaps\apps.py:8 +msgid "Site Maps" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\staticfiles\apps.py:9 +msgid "Static Files" +msgstr "" + +#: .\venv\Lib\site-packages\django\contrib\syndication\apps.py:7 +msgid "Syndication" +msgstr "" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: .\venv\Lib\site-packages\django\core\paginator.py:30 +msgid "…" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:70 +msgid "Enter a valid domain name." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:155 +#: .\venv\Lib\site-packages\django\forms\fields.py:768 +msgid "Enter a valid URL." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:216 +msgid "Enter a valid integer." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:227 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: .\venv\Lib\site-packages\django\core\validators.py:310 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:318 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:330 +#: .\venv\Lib\site-packages\django\core\validators.py:339 +#: .\venv\Lib\site-packages\django\core\validators.py:353 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2215 +#, python-format +msgid "Enter a valid %(protocol)s address." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:332 +msgid "IPv4" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:341 +#: .\venv\Lib\site-packages\django\utils\ipv6.py:30 +msgid "IPv6" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:355 +msgid "IPv4 or IPv6" +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:396 +msgid "Enter only digits separated by commas." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:402 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:437 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:446 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:455 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:462 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:494 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:512 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:535 +#: .\venv\Lib\site-packages\django\forms\fields.py:359 +#: .\venv\Lib\site-packages\django\forms\fields.py:398 +msgid "Enter a number." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:537 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:542 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:547 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\core\validators.py:618 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\core\validators.py:680 +msgid "Null characters are not allowed." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\base.py:1571 +#: .\venv\Lib\site-packages\django\forms\models.py:908 +msgid "and" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\base.py:1573 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\constraints.py:22 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:134 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:135 +msgid "This field cannot be null." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:136 +msgid "This field cannot be blank." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:137 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:141 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:180 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1156 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1157 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1159 +msgid "Boolean (Either True or False)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1209 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1211 +msgid "String (unlimited)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1320 +msgid "Comma-separated integers" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1421 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1425 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1560 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1429 +msgid "Date (without time)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1556 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1564 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1569 +msgid "Date (with time)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1696 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1698 +msgid "Decimal number" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1859 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1863 +msgid "Duration" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1915 +msgid "Email address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:1940 +msgid "File path" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2018 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2020 +msgid "Floating point number" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2060 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2062 +msgid "Integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2158 +msgid "Big (8 byte) integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2175 +msgid "Small integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2183 +msgid "IPv4 address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2214 +msgid "IP address" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2305 +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2306 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2308 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2359 +msgid "Positive big integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2374 +msgid "Positive integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2389 +msgid "Positive small integer" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2405 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2441 +msgid "Text" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2521 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2525 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2529 +msgid "Time" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2637 +msgid "URL" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2661 +msgid "Raw binary data" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2726 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\__init__.py:2728 +msgid "Universally unique identifier" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\files.py:244 +msgid "File" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\files.py:420 +msgid "Image" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\json.py:24 +msgid "A JSON object" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\json.py:26 +msgid "Value must be valid JSON." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:932 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:934 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1228 +msgid "One-to-one relationship" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1285 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1287 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: .\venv\Lib\site-packages\django\db\models\fields\related.py:1335 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: .\venv\Lib\site-packages\django\forms\boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:95 +msgid "This field is required." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:308 +msgid "Enter a whole number." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:479 +#: .\venv\Lib\site-packages\django\forms\fields.py:1260 +msgid "Enter a valid date." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:502 +#: .\venv\Lib\site-packages\django\forms\fields.py:1261 +msgid "Enter a valid time." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:529 +msgid "Enter a valid date/time." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:563 +msgid "Enter a valid duration." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:564 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:633 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:634 +msgid "No file was submitted." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:635 +msgid "The submitted file is empty." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:637 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:642 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:710 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:882 +#: .\venv\Lib\site-packages\django\forms\fields.py:968 +#: .\venv\Lib\site-packages\django\forms\models.py:1592 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:970 +#: .\venv\Lib\site-packages\django\forms\fields.py:1089 +#: .\venv\Lib\site-packages\django\forms\models.py:1590 +msgid "Enter a list of values." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1090 +msgid "Enter a complete value." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1329 +msgid "Enter a valid UUID." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\fields.py:1359 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: .\venv\Lib\site-packages\django\forms\forms.py:94 +msgid ":" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\forms.py:230 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:484 +#: .\venv\Lib\site-packages\django\forms\formsets.py:491 +msgid "Order" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\formsets.py:499 +msgid "Delete" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:901 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:906 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:913 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:922 +msgid "Please correct the duplicate values below." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1359 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1450 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\models.py:1594 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\utils.py:227 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:461 +msgid "Clear" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:462 +msgid "Currently" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:463 +msgid "Change" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:800 +msgid "Unknown" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:801 +msgid "Yes" +msgstr "" + +#: .\venv\Lib\site-packages\django\forms\widgets.py:802 +msgid "No" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:873 +msgid "yes,no,maybe" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:903 +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:920 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:922 +#, python-format +msgid "%s KB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:924 +#, python-format +msgid "%s MB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:926 +#, python-format +msgid "%s GB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:928 +#, python-format +msgid "%s TB" +msgstr "" + +#: .\venv\Lib\site-packages\django\template\defaultfilters.py:930 +#, python-format +msgid "%s PB" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:74 +msgid "p.m." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:75 +msgid "a.m." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:80 +msgid "PM" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:81 +msgid "AM" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:153 +msgid "midnight" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dateformat.py:155 +msgid "noon" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:7 +msgid "Monday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:8 +msgid "Tuesday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:9 +msgid "Wednesday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:10 +msgid "Thursday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:11 +msgid "Friday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:12 +msgid "Saturday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:13 +msgid "Sunday" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:16 +msgid "Mon" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:17 +msgid "Tue" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:18 +msgid "Wed" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:19 +msgid "Thu" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:20 +msgid "Fri" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:21 +msgid "Sat" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:22 +msgid "Sun" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:25 +msgid "January" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:26 +msgid "February" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:27 +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:28 +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:29 +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:30 +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:31 +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:32 +msgid "August" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:33 +msgid "September" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:34 +msgid "October" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:35 +msgid "November" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:36 +msgid "December" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:39 +msgid "jan" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:40 +msgid "feb" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:41 +msgid "mar" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:42 +msgid "apr" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:43 +msgid "may" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:44 +msgid "jun" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:45 +msgid "jul" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:46 +msgid "aug" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:47 +msgid "sep" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:48 +msgid "oct" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:49 +msgid "nov" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:50 +msgid "dec" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:55 +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:69 +msgctxt "alt. month" +msgid "March" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\text.py:75 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\text.py:286 +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +#: .\venv\Lib\site-packages\django\utils\text.py:305 +#: .\venv\Lib\site-packages\django\utils\timesince.py:135 +msgid ", " +msgstr "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\utils\timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:44 +msgid "No year specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:64 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:115 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:214 +msgid "Date out of range" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:94 +msgid "No month specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:147 +msgid "No day specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:194 +msgid "No week specified" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:349 +#: .\venv\Lib\site-packages\django\views\generic\dates.py:380 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:652 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\dates.py:692 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\generic\list.py:169 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\static.py:68 +#: .\venv\Lib\site-packages\django\views\templates\directory_index.html:8 +#: .\venv\Lib\site-packages\django\views\templates\directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:7 +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:204 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:208 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:217 +msgid "Django Documentation" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:218 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:226 +msgid "Tutorial: A Polling App" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:227 +msgid "Get started with Django" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:235 +msgid "Django Community" +msgstr "" + +#: .\venv\Lib\site-packages\django\views\templates\default_urlconf.html:236 +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/manage.py b/manage.py index b67adc1..563eacd 100644 --- a/manage.py +++ b/manage.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" import os import sys @@ -9,7 +7,6 @@ def main(): - """Run administrative tasks.""" os.environ.setdefault( "DJANGO_SETTINGS_MODULE", diff --git a/media/avatars/1727198193049.jpg b/media/avatars/1727198193049.jpg new file mode 100644 index 0000000..9214f00 Binary files /dev/null and b/media/avatars/1727198193049.jpg differ diff --git a/media/avatars/DALLE_2024-09-27_14_PN7dbHS.30.15_-_A_young_man_with_short_hair_and_glasses_wearing_a_formal_business_suit._The_background_is_a_professional_conference_room_setting_with_modern_lightin.webp b/media/avatars/DALLE_2024-09-27_14_PN7dbHS.30.15_-_A_young_man_with_short_hair_and_glasses_wearing_a_formal_business_suit._The_background_is_a_professional_conference_room_setting_with_modern_lightin.webp new file mode 100644 index 0000000..ff9cc12 Binary files /dev/null and b/media/avatars/DALLE_2024-09-27_14_PN7dbHS.30.15_-_A_young_man_with_short_hair_and_glasses_wearing_a_formal_business_suit._The_background_is_a_professional_conference_room_setting_with_modern_lightin.webp differ diff --git a/requirements.txt b/requirements.txt index 73ed40d..d7a152b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,9 @@ markdown2==2.5.1 pillow==10.4.0 psycopg2-binary==2.9.10 python-dotenv==1.0.1 +django-unfold +django-modeltranslation==0.18.11 +drf-spectacular==0.27.1 +drf-spectacular-sidecar==2024.3.4 +django-rosetta==0.10.0 +django-ckeditor-5==0.2.13 diff --git a/search_system.md b/search_system.md new file mode 100644 index 0000000..303e55e --- /dev/null +++ b/search_system.md @@ -0,0 +1,489 @@ +Для создания продвинутой поисковой системы в Django есть несколько возможностей, начиная с базового поиска с использованием фильтрации по полям и заканчивая интеграцией с мощными поисковыми движками, такими как Elasticsearch или Solr. Вот несколько основных методов и подходов для разных уровней сложности: + +### 1. **Базовый поиск с использованием `icontains` в `filter`** + Это самый простой способ поиска, где вы можете использовать `QuerySet` для фильтрации объектов по полям: + + ```python + # models.py + from django.db import models + + class Post(models.Model): + title = models.CharField(max_length=200) + content = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + + # views.py + from django.shortcuts import render + from .models import Post + + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = Post.objects.filter(title__icontains=query) | Post.objects.filter(content__icontains=query) + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + + Здесь `icontains` позволяет осуществлять поиск по подстроке без учета регистра. + +### 2. **Поиск с использованием библиотеки Django ORM для полнотекстового поиска** + + Django поддерживает встроенный полнотекстовый поиск для баз данных PostgreSQL, начиная с Django 1.10. Этот метод позволяет вам находить документы по релевантности. + + ```python + # views.py + from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector + from .models import Post + + def search_posts(request): + query = request.GET.get('q', '') + if query: + vector = SearchVector('title', 'content') + search_query = SearchQuery(query) + posts = Post.objects.annotate(rank=SearchRank(vector, search_query)).filter(rank__gte=0.3).order_by('-rank') + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + +### 3. **Использование Django `django-filter` для фильтрации** + `django-filter` помогает создать форму поиска и фильтрации с использованием Django ORM, включая фильтрацию по дате и другим полям. + + ```python + # filters.py + import django_filters + from .models import Post + + class PostFilter(django_filters.FilterSet): + title = django_filters.CharFilter(lookup_expr='icontains') + content = django_filters.CharFilter(lookup_expr='icontains') + created_at = django_filters.DateFromToRangeFilter() + + class Meta: + model = Post + fields = ['title', 'content', 'created_at'] + ``` + + В views: + + ```python + # views.py + from django.shortcuts import render + from .models import Post + from .filters import PostFilter + + def search_posts(request): + post_filter = PostFilter(request.GET, queryset=Post.objects.all()) + return render(request, 'search_results.html', {'filter': post_filter}) + ``` + +### 4. **Расширенный поиск с использованием Elasticsearch** + Если вам нужно поддерживать масштабируемый и быстрый поиск по сложным запросам, то стоит использовать Elasticsearch. Для интеграции можно воспользоваться библиотекой `django-elasticsearch-dsl`. + + 1. Установите Elasticsearch и `django-elasticsearch-dsl`: + + ```bash + pip install django-elasticsearch-dsl + ``` + + 2. Настройте индекс для модели `Post`. + + ```python + # documents.py + from django_elasticsearch_dsl import Document + from django_elasticsearch_dsl.registries import registry + from .models import Post + + @registry.register_document + class PostDocument(Document): + class Index: + name = 'posts' + + class Django: + model = Post + fields = [ + 'title', + 'content', + 'created_at', + ] + ``` + + 3. В views можно использовать `PostDocument` для поиска: + + ```python + # views.py + from django.shortcuts import render + from .documents import PostDocument + + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = PostDocument.search().query("multi_match", query=query, fields=['title', 'content']) + else: + posts = PostDocument.search() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + +### 5. **Фильтрация с использованием Django Haystack** + Haystack – это еще один инструмент для создания поисковых систем, который поддерживает несколько движков, включая Elasticsearch, Solr и Whoosh. + + 1. Установите Django Haystack и поисковый движок (например, Whoosh): + + ```bash + pip install django-haystack whoosh + ``` + + 2. Настройте индекс в `search_indexes.py`: + + ```python + # search_indexes.py + from haystack import indexes + from .models import Post + + class PostIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, use_template=True) + title = indexes.CharField(model_attr='title') + content = indexes.CharField(model_attr='content') + + def get_model(self): + return Post + ``` + + 3. Создайте шаблон для индексации данных (`templates/search/indexes/yourapp/post_text.txt`): + + ```jinja + {{ object.title }} + {{ object.content }} + ``` + + 4. Настройте поиск в views: + + ```python + from haystack.query import SearchQuerySet + from django.shortcuts import render + + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = SearchQuerySet().filter(content=query) + else: + posts = SearchQuerySet().all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + +### Заключение +Выбор подхода зависит от ваших требований и масштабов проекта: + +- Для небольших и средних проектов базовый поиск и фильтрация через `icontains` или `django-filter` может быть достаточным. +- Для продвинутого полнотекстового поиска стоит использовать PostgreSQL или Elasticsearch. +- Для комплексных решений с поддержкой различных движков лучше использовать Haystack. + +--- +Если вы хотите использовать Elasticsearch или PostgreSQL для продвинутого поиска, вам нужно учитывать следующие моменты: + +1. **PostgreSQL полнотекстовый поиск** будет работать только с PostgreSQL. Если вы поменяете базу данных на SQLite, MySQL или другую, этот метод перестанет работать, так как полнотекстовый поиск на уровне базы данных — это функция, специфичная для PostgreSQL. + +2. **Elasticsearch** же работает независимо от базы данных, потому что это отдельный поисковый движок, а не часть вашей основной базы данных. Это значит, что вы можете использовать любую базу данных (включая SQLite) вместе с Elasticsearch, так как данные для поиска хранятся в индексе Elasticsearch и не зависят от структуры самой базы данных. + +### Как это настроить, чтобы работало с любой базой данных + +#### 1. **Использование Elasticsearch как основного поискового инструмента** + Вы можете использовать любую базу данных для хранения основной информации, а для поиска по этой информации полагаться на Elasticsearch. Процесс будет выглядеть так: + + 1. **Подключение и настройка Elasticsearch.** Убедитесь, что Elasticsearch установлен и настроен. После этого нужно создать документ, который будет индексировать вашу модель Django. + + ```python + # documents.py + from django_elasticsearch_dsl import Document + from django_elasticsearch_dsl.registries import registry + from .models import Post + + @registry.register_document + class PostDocument(Document): + class Index: + name = 'posts' + + class Django: + model = Post + fields = [ + 'title', + 'content', + 'created_at', + ] + ``` + + 2. **Настройка views для поиска с Elasticsearch.** После того, как документ настроен, создайте представление (`view`), которое будет отправлять поисковый запрос в Elasticsearch. + + ```python + # views.py + from django.shortcuts import render + from .documents import PostDocument + + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = PostDocument.search().query("multi_match", query=query, fields=['title', 'content']) + else: + posts = PostDocument.search() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + + 3. **Автоматическое обновление индексов Elasticsearch при изменении данных.** Если данные в базе данных изменяются, необходимо синхронизировать их с Elasticsearch, чтобы индексы всегда были актуальны. Для этого используйте сигналы (`signals`) или настройте пакет `django-elasticsearch-dsl` для автоматической синхронизации. + +#### 2. **Fallback для баз данных без полнотекстового поиска** + Если Elasticsearch по какой-то причине недоступен, вы можете использовать базовую фильтрацию с помощью `icontains` для других баз данных, например: + + ```python + # views.py + from django.shortcuts import render + from django.conf import settings + from .documents import PostDocument + from .models import Post + + def search_posts(request): + query = request.GET.get('q', '') + if query: + if settings.USE_ELASTICSEARCH: # Используем Elasticsearch + posts = PostDocument.search().query("multi_match", query=query, fields=['title', 'content']) + else: # Используем базовую фильтрацию с icontains + posts = Post.objects.filter(title__icontains=query) | Post.objects.filter(content__icontains=query) + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + +### Преимущества и недостатки подхода + +| Подход | Преимущества | Недостатки | +|------------------------------|--------------------------------------------------------|-----------------------------------------------------| +| **Elasticsearch** | Независимость от базы данных, мощный и гибкий поиск | Требуется отдельная установка и поддержка сервера | +| **PostgreSQL полнотекстовый**| Удобная интеграция с Django ORM, нет доп. зависимостей | Поддерживается только PostgreSQL | +| **SQLite / MySQL + icontains** | Легко настраивается, работает с любой БД | Ограниченный поиск, менее точные и гибкие результаты| + +Таким образом, оптимально использовать Elasticsearch как независимый поисковый движок для обеспечения максимальной гибкости и совместимости с любой базой данных. + +--- +Да, вы правы — для использования Elasticsearch требуется либо установка его на компьютер, либо поднятие контейнера Docker. Если хотите обойтись без установки Elasticsearch, но всё равно нужен продвинутый поиск, вот несколько вариантов: + +### Варианты для обхода Elasticsearch + +1. **Использование PostgreSQL для полнотекстового поиска (только PostgreSQL)**: + Если ваша база данных — PostgreSQL, его встроенные инструменты для полнотекстового поиска предоставляют хорошие возможности, хотя и менее мощные, чем у Elasticsearch. + + ```python + # views.py + from django.contrib.postgres.search import SearchVector + from .models import Post + + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = Post.objects.annotate(search=SearchVector('title', 'content')).filter(search=query) + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + + Этот подход работает на уровне базы данных и не требует установки дополнительных сервисов, но он совместим только с PostgreSQL. + +2. **Использование Django ORM с `__icontains` для упрощённого поиска**: + Это самый простой способ и не требует использования каких-либо специфичных баз данных. Этот метод не столь мощный, как Elasticsearch, но позволит осуществить базовый поиск по текстовым полям. + + ```python + def search_posts(request): + query = request.GET.get('q', '') + if query: + posts = Post.objects.filter(title__icontains=query) | Post.objects.filter(content__icontains=query) + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) + ``` + + Этот метод работает с любой базой данных (SQLite, MySQL, PostgreSQL и др.) и позволяет организовать простой поиск, но без ранжирования или сложных запросов. + +3. **Использование Django-Haystack (совместим с SQLite)**: + Библиотека Django Haystack поддерживает несколько поисковых движков, включая базовые текстовые индексы. Это позволит реализовать простой индексный поиск, совместимый с SQLite и другими базами. + + ```bash + pip install django-haystack + ``` + + Пример настройки Haystack с SQLite: + + ```python + # settings.py + INSTALLED_APPS = [ + # ..., + 'haystack', + ] + + HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', + }, + } + ``` + + После этого можно создать индекс для модели и использовать Haystack для поиска. Этот метод подходит для базового поиска, но с ограниченной функциональностью. + +### Какой вариант лучше выбрать + +- Если у вас **PostgreSQL**, лучше всего использовать его встроенные средства полнотекстового поиска. +- Если ваша база данных **SQLite** или другая, и вам нужен продвинутый поиск, **Django-Haystack с SimpleEngine** будет наилучшим выбором для базового поиска без установки дополнительных сервисов. +- Если достаточно простого поиска и у вас ограниченные требования к функциональности, используйте **`icontains`** — это самый простой и универсальный метод. + +Каждый из этих методов позволит вам обойтись без установки Elasticsearch, обеспечивая подходящий уровень поиска для вашего проекта. +--- +В Django тоже можно реализовать полнотекстовый поиск с PostgreSQL, используя его встроенные возможности и интеграцию с `django.contrib.postgres.search`. Это приложение добавляет поддержку полнотекстового поиска для полей в PostgreSQL и обеспечивает удобные классы и функции, чтобы интегрировать поисковые запросы на уровне ORM. Вот полное объяснение того, как это сделать. + +### 1. Установка PostgreSQL в качестве основной базы данных + +Прежде чем использовать полнотекстовый поиск, нужно убедиться, что Django настроен на использование PostgreSQL: + +```python +# settings.py +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'your_db_name', + 'USER': 'your_db_user', + 'PASSWORD': 'your_password', + 'HOST': 'localhost', + 'PORT': '5432', + } +} +``` + +### 2. Основные классы и функции для полнотекстового поиска + +Django предоставляет несколько ключевых классов и функций для работы с полнотекстовым поиском в PostgreSQL: + +- `SearchVector`: позволяет выбрать, какие поля модели использовать для поиска. +- `SearchQuery`: создает поисковый запрос. +- `SearchRank`: обеспечивает ранжирование результатов поиска. +- `SearchHeadline`: выделяет поисковые термины в результатах. + +### 3. Пример модели и конфигурации полнотекстового поиска + +Допустим, у нас есть модель `Post` с полями `title` и `content`, и мы хотим выполнить полнотекстовый поиск по этим полям. + +```python +# models.py +from django.db import models + +class Post(models.Model): + title = models.CharField(max_length=255) + content = models.TextField() + + def __str__(self): + return self.title +``` + +### 4. Настройка простого поиска с `SearchVector` и `SearchQuery` + +Теперь можно создать представление, которое будет искать по полям `title` и `content` с использованием `SearchVector` и `SearchQuery`. + +```python +# views.py +from django.contrib.postgres.search import SearchVector, SearchQuery +from django.shortcuts import render +from .models import Post + +def search_posts(request): + query = request.GET.get('q') + if query: + search_vector = SearchVector('title', 'content') + search_query = SearchQuery(query) + posts = Post.objects.annotate(search=search_vector).filter(search=search_query) + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) +``` + +- `SearchVector('title', 'content')` объединяет оба поля в один вектор для поиска. +- `SearchQuery(query)`: принимает поисковый запрос, который мы будем сравнивать с вектором. + +### 5. Ранжирование результатов поиска с `SearchRank` + +Для сортировки результатов по релевантности используем `SearchRank`. + +```python +from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank + +def search_posts(request): + query = request.GET.get('q') + if query: + search_vector = SearchVector('title', 'content') + search_query = SearchQuery(query) + posts = Post.objects.annotate( + rank=SearchRank(search_vector, search_query) + ).filter(search=search_query).order_by('-rank') + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) +``` + +Теперь результаты будут отсортированы по релевантности (от более релевантных к менее релевантным). + +### 6. Выделение ключевых слов в результатах с `SearchHeadline` + +`SearchHeadline` можно использовать для выделения поисковых терминов в тексте. Это может быть полезно для подсветки ключевых слов в результатах поиска. + +```python +from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank, SearchHeadline + +def search_posts(request): + query = request.GET.get('q') + if query: + search_vector = SearchVector('title', 'content') + search_query = SearchQuery(query) + posts = Post.objects.annotate( + rank=SearchRank(search_vector, search_query), + headline=SearchHeadline('content', search_query) # Добавляем выделение слов + ).filter(search=search_query).order_by('-rank') + else: + posts = Post.objects.all() + return render(request, 'search_results.html', {'posts': posts, 'query': query}) +``` + +Теперь в каждом объекте `Post` будет атрибут `headline`, в котором выделены найденные слова. + +### 7. Использование разных языков и конфигураций поиска + +По умолчанию используется английская конфигурация. Чтобы изменить это, можно передать дополнительный аргумент: + +```python +search_vector = SearchVector('title', 'content', config='russian') +search_query = SearchQuery(query, config='russian') +``` + +### Полный пример шаблона + +```html + +

Search Results for "{{ query }}"

+{% for post in posts %} +
+

{{ post.title }}

+

{{ post.headline|safe }}

+
+{% empty %} +

No results found.

+{% endfor %} +``` + +### Преимущества и ограничения + +#### Преимущества: +- **Глубокая интеграция** с ORM Django. +- Поддержка **ранжирования и выделения** ключевых слов. +- **Настройка языка** для корректной обработки разных языков. + +#### Ограничения: +- Поддерживается только **PostgreSQL**. +- Полнотекстовый поиск требует **GIN-индексации** для оптимизации скорости. + +### Заключение + +Используя `django.contrib.postgres.search`, можно организовать мощную систему полнотекстового поиска прямо на уровне ORM Django, включая ранжирование, подсветку ключевых слов и настройку языков. \ No newline at end of file diff --git a/static/css/tailwind.css b/static/css/tailwind.css new file mode 100644 index 0000000..5b0c2f7 --- /dev/null +++ b/static/css/tailwind.css @@ -0,0 +1,945 @@ +/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/ +*, :after, :before { + box-sizing: border-box; + border: 0 solid #e5e7eb +} + +:after, :before { + --tw-content: "" +} + +:host, html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + font-feature-settings: normal; + font-variation-settings: normal; + -webkit-tap-highlight-color: transparent +} + +body { + margin: 0; + line-height: inherit +} + +hr { + height: 0; + color: inherit; + border-top-width: 1px +} + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted +} + +h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: inherit +} + +a { + color: inherit; + text-decoration: inherit +} + +b, strong { + font-weight: bolder +} + +code, kbd, pre, samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace; + font-feature-settings: normal; + font-variation-settings: normal; + font-size: 1em +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: initial +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse +} + +button, input, optgroup, select, textarea { + font-family: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + font-size: 100%; + font-weight: inherit; + line-height: inherit; + letter-spacing: inherit; + color: inherit; + margin: 0; + padding: 0 +} + +button, select { + text-transform: none +} + +button, input:where([type=button]), input:where([type=reset]), input:where([type=submit]) { + -webkit-appearance: button; + background-color: initial; + background-image: none +} + +:-moz-focusring { + outline: auto +} + +:-moz-ui-invalid { + box-shadow: none +} + +progress { + vertical-align: initial +} + +::-webkit-inner-spin-button, ::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +summary { + display: list-item +} + +blockquote, dd, dl, figure, h1, h2, h3, h4, h5, h6, hr, p, pre { + margin: 0 +} + +fieldset { + margin: 0 +} + +fieldset, legend { + padding: 0 +} + +menu, ol, ul { + list-style: none; + margin: 0; + padding: 0 +} + +dialog { + padding: 0 +} + +textarea { + resize: vertical +} + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + color: #9ca3af +} + +input::placeholder, textarea::placeholder { + opacity: 1; + color: #9ca3af +} + +[role=button], button { + cursor: pointer +} + +:disabled { + cursor: default +} + +audio, canvas, embed, iframe, img, object, svg, video { + display: block; + vertical-align: middle +} + +img, video { + max-width: 100%; + height: auto +} + +[hidden] { + display: none +} + +*, ::backdrop, :after, :before { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #3b82f680; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: +} + +.\!container { + width: 100% !important +} + +.container { + width: 100% +} + +@media (min-width: 640px) { + .\!container { + max-width: 640px !important + } + + .container { + max-width: 640px + } +} + +@media (min-width: 768px) { + .\!container { + max-width: 768px !important + } + + .container { + max-width: 768px + } +} + +@media (min-width: 1024px) { + .\!container { + max-width: 1024px !important + } + + .container { + max-width: 1024px + } +} + +@media (min-width: 1280px) { + .\!container { + max-width: 1280px !important + } + + .container { + max-width: 1280px + } +} + +@media (min-width: 1536px) { + .\!container { + max-width: 1536px !important + } + + .container { + max-width: 1536px + } +} + +.visible { + visibility: visible +} + +.collapse { + visibility: collapse +} + +.static { + position: static +} + +.\!fixed { + position: fixed !important +} + +.fixed { + position: fixed +} + +.absolute { + position: absolute +} + +.relative { + position: relative +} + +.sticky { + position: sticky +} + +.left-0 { + left: 0 +} + +.left-5 { + left: 1.25rem +} + +.top-0 { + top: 0 +} + +.z-\[99\] { + z-index: 99 +} + +.float-right { + float: right +} + +.m-2 { + margin: .5rem +} + +.mx-4 { + margin-left: 1rem; + margin-right: 1rem +} + +.mb-1 { + margin-bottom: .25rem +} + +.mb-3 { + margin-bottom: .75rem +} + +.mb-5 { + margin-bottom: 1.25rem +} + +.ml-2 { + margin-left: .5rem +} + +.ml-auto { + margin-left: auto +} + +.mr-2 { + margin-right: .5rem +} + +.mr-auto { + margin-right: auto +} + +.mt-10 { + margin-top: 2.5rem +} + +.mt-3 { + margin-top: .75rem +} + +.mt-4 { + margin-top: 1rem +} + +.mt-5 { + margin-top: 1.25rem +} + +.mb-10 { + margin-bottom: 2.5rem +} + +.mt-2 { + margin-top: .5rem +} + +.block { + display: block +} + +.inline-block { + display: inline-block +} + +.inline { + display: inline +} + +.flex { + display: flex +} + +.inline-flex { + display: inline-flex +} + +.table { + display: table +} + +.table-caption { + display: table-caption +} + +.table-cell { + display: table-cell +} + +.grid { + display: grid +} + +.contents { + display: contents +} + +.\!hidden { + display: none !important +} + +.hidden { + display: none +} + +.size-2 { + width: .5rem; + height: .5rem +} + +.h-1 { + height: .25rem +} + +.h-14 { + height: 3.5rem +} + +.h-16 { + height: 4rem +} + +.h-3 { + height: .75rem +} + +.h-8 { + height: 2rem +} + +.h-\[100vh\] { + height: 100vh +} + +.max-h-\[400px\] { + max-height: 400px +} + +.w-\[100vw\] { + width: 100vw +} + +.w-\[90\%\] { + width: 90% +} + +.w-full { + width: 100% +} + +.max-w-\[500px\] { + max-width: 500px +} + +.max-w-\[90vw\] { + max-width: 90vw +} + +.max-w-\[20px\] { + max-width: 20px +} + +.max-w-\[300px\] { + max-width: 300px +} + +.flex-shrink, .shrink { + flex-shrink: 1 +} + +.flex-grow, .grow { + flex-grow: 1 +} + +.border-collapse { + border-collapse: collapse +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) +} + +.cursor-pointer { + cursor: pointer +} + +.resize { + resize: both +} + +.grid-cols-1 { + grid-template-columns:repeat(1, minmax(0, 1fr)) +} + +.grid-cols-2 { + grid-template-columns:repeat(2, minmax(0, 1fr)) +} + +.flex-col { + flex-direction: column +} + +.flex-wrap { + flex-wrap: wrap +} + +.items-center { + align-items: center +} + +.justify-end { + justify-content: flex-end +} + +.justify-center { + justify-content: center +} + +.gap-10 { + gap: 2.5rem +} + +.gap-8 { + gap: 2rem +} + +.gap-2 { + gap: .5rem +} + +.gap-5 { + gap: 1.25rem +} + +.gap-x-3 { + -moz-column-gap: .75rem; + column-gap: .75rem +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap +} + +.rounded { + border-radius: .25rem +} + +.rounded-xl { + border-radius: .75rem +} + +.border { + border-width: 1px +} + +.border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0/var(--tw-border-opacity)) +} + +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246/var(--tw-bg-opacity)) +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235/var(--tw-bg-opacity)) +} + +.bg-orange-500 { + --tw-bg-opacity: 1; + background-color: rgb(249 115 22/var(--tw-bg-opacity)) +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255/var(--tw-bg-opacity)) +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68/var(--tw-bg-opacity)) +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgb(156 163 175/var(--tw-bg-opacity)) +} + +.bg-gray-700 { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81/var(--tw-bg-opacity)) +} + +.bg-blue-400 { + --tw-bg-opacity: 1; + background-color: rgb(96 165 250/var(--tw-bg-opacity)) +} + +.bg-green-400 { + --tw-bg-opacity: 1; + background-color: rgb(74 222 128/var(--tw-bg-opacity)) +} + +.\!bg-gray-700 { + --tw-bg-opacity: 1 !important; + background-color: rgb(55 65 81/var(--tw-bg-opacity)) !important +} + +.\!bg-orange-500 { + --tw-bg-opacity: 1 !important; + background-color: rgb(249 115 22/var(--tw-bg-opacity)) !important +} + +.\!bg-red-500 { + --tw-bg-opacity: 1 !important; + background-color: rgb(239 68 68/var(--tw-bg-opacity)) !important +} + +.p-1 { + padding: .25rem +} + +.p-14 { + padding: 3.5rem +} + +.p-16 { + padding: 4rem +} + +.p-2 { + padding: .5rem +} + +.p-3 { + padding: .75rem +} + +.p-4 { + padding: 1rem +} + +.p-5 { + padding: 1.25rem +} + +.p-8 { + padding: 2rem +} + +.px-\[40px\] { + padding-left: 40px; + padding-right: 40px +} + +.py-3 { + padding-top: .75rem; + padding-bottom: .75rem +} + +.py-2 { + padding-top: .5rem; + padding-bottom: .5rem +} + +.px-3 { + padding-left: .75rem; + padding-right: .75rem +} + +.text-center { + text-align: center +} + +.indent-1 { + text-indent: .25rem +} + +.text-\[20px\] { + font-size: 20px +} + +.text-\[25px\] { + font-size: 25px +} + +.text-\[30px\] { + font-size: 30px +} + +.text-\[40px\] { + font-size: 40px +} + +.text-sm { + font-size: .875rem; + line-height: 1.25rem +} + +.text-\[27px\] { + font-size: 27px +} + +.font-\[400\] { + font-weight: 400 +} + +.font-\[500\] { + font-weight: 500 +} + +.font-\[600\] { + font-weight: 600 +} + +.font-bold { + font-weight: 700 +} + +.uppercase { + text-transform: uppercase +} + +.lowercase { + text-transform: lowercase +} + +.capitalize { + text-transform: capitalize +} + +.italic { + font-style: italic +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0/var(--tw-text-opacity)) +} + +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246/var(--tw-text-opacity)) +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68/var(--tw-text-opacity)) +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255/var(--tw-text-opacity)) +} + +.\!text-white { + --tw-text-opacity: 1 !important; + color: rgb(255 255 255/var(--tw-text-opacity)) !important +} + +.text-\[px\] { + color: px +} + +.underline { + text-decoration-line: underline +} + +.line-through { + text-decoration-line: line-through +} + +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.shadow { + --tw-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) +} + +.shadow, .shadow-md { + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color) +} + +.outline { + outline-style: solid +} + +.ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) +} + +.blur { + --tw-blur: blur(8px) +} + +.blur, .grayscale { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) +} + +.grayscale { + --tw-grayscale: grayscale(100%) +} + +.\!invert { + --tw-invert: invert(100%) !important; + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important +} + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) +} + +.\!filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) +} + +.backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia) +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(.4, 0, .2, 1); + transition-duration: .15s +} + +.ease-out { + transition-timing-function: cubic-bezier(0, 0, .2, 1) +} + +.\[a-zA-Z\:\\-\] { + a-z-a--z: \- +} + +@media (min-width: 640px) { + .sm\:grid-cols-2 { + grid-template-columns:repeat(2, minmax(0, 1fr)) + } +} + +@media (min-width: 768px) { + .md\:grid-cols-2 { + grid-template-columns:repeat(2, minmax(0, 1fr)) + } + + .md\:grid-cols-3 { + grid-template-columns:repeat(3, minmax(0, 1fr)) + } +} + +@media (min-width: 1024px) { + .lg\:grid-cols-4 { + grid-template-columns:repeat(4, minmax(0, 1fr)) + } +} \ No newline at end of file diff --git a/static/images/django-logo.png b/static/images/django-logo.png new file mode 100644 index 0000000..81ddb85 Binary files /dev/null and b/static/images/django-logo.png differ diff --git a/static/images/login.jpg b/static/images/login.jpg new file mode 100644 index 0000000..b6bdc5f Binary files /dev/null and b/static/images/login.jpg differ diff --git a/templates/blog/home.html b/templates/blog/home.html index 05b72dc..72db7ba 100644 --- a/templates/blog/home.html +++ b/templates/blog/home.html @@ -1,4 +1,4 @@ - +{% load blog_tags %}