{ "info": { "author": "Jordan Kowal", "author_email": "kowaljordan@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Topic :: Software Development :: Build Tools" ], "description": "# Django Database Translation\n\n## **DESCRIPTION**\nNatively, Django only handles hardcoded text translation. This package aims to assist you in translating the entries of your database. It is fairly easy to setup and use, as demonstrated below.\n\nThe three main actions of the program are:\n- Automatically generates database entries for any field that must be translated\n- Allows you to access the translations directly from your model's admin\n- Easily translate your instances before pushing them in your template/context/json\n\n**Please note, this does not TRANSLATE for you**. It creates the entries in the database for your translations, which you have to fill. Here's how it works in practice:\n\n- I have registered 3 languages: French, English, Italian\n- I have registered the \"Project\" model and said it must be translated\n- I have registered 2 fields within the Project model, \"name\" and \"description\", as field that will require translations\n- Whenever I create a new project, 6 new translation entries will be create: \"name\" in 3 languages, and \"description\" in 3 languages.\n- If I go in my \"Project\" admin, I will be able to directly edit those 6 translations\n- To use translations in the frontend, simply translate your object using our utils functions before pushing them in your context/template/JSON\n\n**Note that this package works RETROACTIVELY**. Translation entries will be generated for all existing instances when deploying this app\n\n## **WHAT IT CONTAINS**\nHere's a quick recap of what this package contains:\n\n- 4 new database tables:\n - `Field` (ddt_fields)\n - `Item` (ddt_items)\n - `Language` (ddt_languages)\n - `Translation` (ddt_translations)\n- A few class extenders/templates:\n - `TranslatedField` which is a Field that will be used for any field that must be translated\n - `TranslatedModel` to extend any model that has at least one `TranslatedField`\n - `TranslatedAdmin` to extend any admin whose model inherits from `TranslatedModel`\n- A few tools for language selection:\n - `LanguageSelection`, a form that can be used in the frontend to pick a language\n - `update_user_language`, a function that updates the user language for both django and our app\n- A few tools for getting translations\n - `all_instances_as_translated_dict`: Applies 'instance_as_translated_dict' to the iterable of instances\n - `get_language_from_session`: Returns the Language instance used by our user, or \"False\" if none is found\n - `instance_as_translated_dict`: Returns a model instance into a dict containing all of its fields\n\nIt contains other elements, but this is what you will be using 99% of the time.\n\n## **SETUP**\n\n### **1. Installation**\nFirst, we must install the package:\n\n- Install the package with `pip install django-database-translation`\n- In `settings.py`, in your `INSTALLED_APPS`, add `\"django_database_translation\"` (note that we are using **underscores** this time)\n- Then run the `python manage.py makemigrations django_database_translation` command\n- Then run the `python manage.py migrate django_database_translation` command to create the 4 new tables\n\n### **2. Updating your models**\nThe point here is to \"flag\" which models have fields that must be translated. As a result, we will **extend** the models, and **change** the fields:\n\n- Extend a model using `TranslatedModel` for any model that contains TranslatedFields\n- Change any field that will be translated to a `TranslatedField` (it is a ForeignKey to our Item model)\n\nHere's an example of code:\n\n```python\nfrom datetime import datetime\nfrom django.db import models\nfrom django_database_translation.fields import TranslatedField\nfrom django_database_translation.models import TranslatedModel\n\nclass Project(TranslatedModel): # <-- Extended class\n title = TranslatedField( # <-- Field changed\n related_name=\"portfolio_project_title\",\n verbose_name=\"Title\"\n )\n description = TranslatedField( # <-- Field changed\n related_name=\"portfolio_project_description\",\n verbose_name=\"Description\"\n )\n date_posted = models.DateField(\n db_index=True,\n default=datetime.now,\n help_text=\"Date displayed on the frontend\",\n null=False,\n verbose_name=\"Posted on\",\n )\n```\n\nOnce you're all done, run the `makemigrations` and `migrate` commands to update your own database with your new changes.\n\n### **3. Updating your admins**\nNow that our models have been updated, we can update their admins. Note that you must only update the admins of models that have been extended with `TranslatedModel`.\n\nSimply extend your admin using the `TranslatedAdmin` class:\n\n```python\nfrom django.contrib import admin\nfrom django_database_translation.admin import TranslatedAdmin\nfrom .models import Project\n\n@admin.register(Project)\nclass ProjectAdmin(TranslatedAdmin): # <-- Extended class\n # Your code here\n```\n\nNow you will be able to edit translations directly from your admins.\n\n### **4. Manually fill Language and Field in the admin**\nNow we need to manually create a few entries in both our `Language` and `Field` models. Do not worry about `Item` and `Translation`, their content will be generated automatically.\n\n#### **--> Language**\nIn this table, simply create all the languages available on your website. Make sure that the `django_language_name` matches an entry in `LANGUAGES` from `settings.py`.\n\n```python\n# If settings.py is like this:\nLANGUAGES = (\n (\"fr-fr\", \"Fran\u00e7ais\"),\n (\"en-us\", \"English\"),\n)\n# Make sure your \"django_language_name\" is either \"fr-fr\" or \"en-us\"\n```\n\n#### **--> Field**\nHere you must simply register all the fields you've changed to `TranslatedField`. Make sure their name matches the actual name of the field.\n\n### **5. Translate your entries**\nNow that everything is setup, you can go in your admin and go in any of your database entry. If it is a model that uses `TranslatedModel` and its admin is `TranslatedAdmin`, you'll be able to see the translations directly in its admin. Go ahead and translate anything that must be translated.\n\n## **SHOWING TRANSLATIONS**\n### **1. Updating the user language**\nSince you're translating your database, you are probably also translating all of your HTML/Python texts, meaning you are using django built-in `i18n` and `translation` modules (gettext, etc.). To make life easier, we created the `update_user_language` function. It will update both django and our module current language for this user. Here's how it works:\n- Have a form where the user can choose its language\n- Make sure to use our `Language` entries as available choices\n- When the users POST the form (and `form.is_valid()`), call this function with the request object and the chosen Language id.\n- The session will be updated using your Language `instance.django_language_name`\n\nExample:\n\n```python\n# We call this when the form is valid\ndef form_valid(self, form):\n # (...)\n language_id = form.cleaned_data['language_id']\n update_user_language(self.request, language_id=language_id)\n # (...)\n```\n\nTo gain some time, you can use the `LanguageSelection` form as your form for the language selection. It is a form with:\n- Only one field: the language id\n- This field is a ChoiceField\n- The available choices are the Language entries\n- Uses either a RADIO for the form rendering\n\nHere is a full example of a view that is accessible everywhere on a website:\n\n```python\nfrom django.shortcuts import redirecte\nfrom django.views.generic import View\nfrom django_database_translation.forms import LanguageSelection\nfrom django_database_translation.utils import update_user_language\n\nclass UpdateLanguageSelection(View):\n \"\"\"\n View only reachable through POST that allows you to select your language\n A form sending a POST request to this view is available on every page:\n - The form is in our context_processor\n - The main.html renders the form\n \"\"\"\n\n # ----------------------------------------\n # Settings\n # ----------------------------------------\n form_class = LanguageSelection\n\n # ----------------------------------------\n # Core Methods\n # ----------------------------------------\n def get(self, request, *args, **kwargs):\n \"\"\"\n Defines how to handle a GET request.\n In this case, they are not allowed and will be redirected\n \"\"\"\n return self.redirect_to_current_page()\n\n def post(self, request, *args, **kwargs):\n \"\"\"\n Defines how to handle a POST request.\n It will update the user session language.\n \"\"\"\n form = self.form_class(request.POST)\n if form.is_valid():\n response = self.form_valid(form)\n else:\n response = self.form_invalid(form)\n return response\n\n # ----------------------------------------\n # Helper Methods\n # ----------------------------------------\n def form_invalid(self, form):\n \"\"\"Simply redirects to the current page\"\"\"\n return self.redirect_to_current_page()\n\n def form_valid(self, form):\n \"\"\"\n Called if the form is valid and data has been cleaned\n Updates the user current language then redirects him to the current page\n \"\"\"\n language_id = form.cleaned_data['language_id']\n update_user_language(self.request, language_id=language_id)\n return self.redirect_to_current_page()\n\n def redirect_to_current_page(self):\n \"\"\"Reloads the current page or redirects you to the homepage\"\"\"\n current_page = self.request.META['HTTP_REFERER']\n if not current_page:\n current_page = \"home\"\n return redirect(current_page)\n```\n\n### **2. Displaying the translations**\nThe last thing left to do is to display the translations. Our instances have keys to `Item`, which then have keys to `Translation`. To get the translations, we need to pair an `Item` with a `Language`.\n\nFirst off, get the language using our `get_language_from_session`. This will return the user's current `Language` instance.\n\nThen, before sending your object(s) in the template/context/JSON, translate them using `instance_as_translated_dict` or `all_instances_as_translated_dict`. Your instances will become dictionaries, and your `Item` keys will automatically be replaced with your translated text. Those two functions can either be given a `language`, or guess it themselves by using the `request` arg. If you're going to make several translations in the same functions, get the `language` first. This will avoid making a new database request each time to guess the language.\n\nYou'll find below an example with a Project model and a Job model, where we:\n- Translate projects by overriding the `get_queryset` method\n- Get and translate jobs by overriding the `get_context_data` method\n- In one case, we get the language THEN translate\n- In the other case, we simply push the request, and the function will guess the language\n- (This is done only as a showcase)\n\n```python\nfrom django.utils.translation import gettext\nfrom django.views.generic import ListView\nfrom django_database_translation.utils import all_instances_as_translated_dict, get_language_from_session\nfrom .models import Job, Project\n\nclass ProjectList(ListView):\n # ----------------------------------------\n # Settings\n # ----------------------------------------\n model = Project\n template_name = \"portfolio/pages/portfolio.html\"\n context_object_name = \"projects\"\n ordering = [\"-date_posted\"]\n\n # ----------------------------------------\n # Overridden Methods\n # ----------------------------------------\n def get_context_data(self, **kwargs):\n \"\"\"\n The method was overridden to do the following:\n - Add the 'meta_title' to the context\n - Add the active 'jobs' to the context and translating their data\n \"\"\"\n # Get the Job instances and translate them using the request\n all_jobs = Job.objects.all()\n jobs = filter(lambda x: x.count_projects(), all_jobs)\n jobs = all_instances_as_translated_dict(jobs, depth=True, request=self.request)\n # Update the context\n context = super(ProjectList, self).get_context_data(**kwargs)\n context.update({\n \"meta_title\": gettext(\"portoflio_meta_title\"),\n \"jobs\": jobs,\n })\n return context\n\n def get_queryset(self):\n # We get the language first, then use it for our translation\n language = get_language_from_session(self.request)\n query = Project.objects.filter(active=True)\n query = all_instances_as_translated_dict(query, depth=True, language=language)\n return query\n```\n\n## **ADDTIONAL INFORMATION**\n\n### **Translate with depth**\nIn the example above, with Project and Job, you'll notice we translated them using `depth=True`. It means that if our `TranslatedModel` has foreign keys to other models, those models will also be translated. In this case, if `Project` has a FK to `Job`, we can acces `Job.name` by:\n- Using `project[\"job\"][\"name\"]` in python (*fields are now keys, not attributes*)\n- Using `project.job.name` in html/templates (*same syntax as normal instances*)\n\n### **How to translate a form that uses the database**\nSometimes, your form will have `ChoiceField` generated from your database. If the table used is a `TranslatedModel`, you'll need to get the right language for your form. Since a form doesn't have access to the `request` object, you need to pass either the `language` or the `request` in your form init method. Here's an example on how to do it:\n\n```python\n# In your FORM, override the __init__ method like so:\ndef __init__(self, *args, **kwargs):\n self.language = kwargs.pop(\"language\", None)\n super(YourFormName, self).__init__(*args, **kwargs)\n\n# In your VIEW, call the form like so:\ndef function_inside_your_view(self):\n # (...)\n request = self.request\n language = get_language_from_session(request)\n form = self.form_class(request.GET, language=language) # (or .POST, etc.)\n # (...)\n```\n\n### **Model.objects.bulk_create()**\nIf a model inherits from `TranslatedModel`, it will not be able to use its `.bulk_create()` method. We are forced deactivate it as our program uses **signals** to work, and `.bulk_create()` does not trigger signals.\n\n### **More info on the utils functions**\nHere's a closer look on the utils functions:\n\n```python\n# --------------------------------------------------------------------------------\n# > Imports\n# --------------------------------------------------------------------------------\nfrom django.db import models\nfrom django.db.models.fields.files import ImageFieldFile, FieldFile\nfrom django.utils.translation import activate, LANGUAGE_SESSION_KEY\nfrom .models import Item, Language, Translation\n\n\n# --------------------------------------------------------------------------------\n# > Functions\n# --------------------------------------------------------------------------------\ndef all_instances_as_translated_dict(instances, depth=True, language=None, request=None):\n \"\"\"\n Description:\n Applies 'instance_as_translated_dict' to the iterable of instances\n Returns a list of dicts which contains the fields of all your instances\n Check the 'instance_as_translated_dict' for more info\n Args:\n instances (iterable): An iterable of your model instances\n depth (bool, optional): Determines if FK will also be transformed into dicts. Defaults to True.\n language (Language, optional): A Language instance from this app. Defaults to None.\n request (HttpRequest, option): HttpRequest from Django. Defaults to None.\n Returns:\n list: A list of dicts, where each dict contains the fields/values of the initial instances\n \"\"\"\n # Checking arguments\n if language is None and request is None:\n raise TypeError(\"You must provide either 'language' or 'request'\")\n # Get the language from the session\n if language is None:\n language = get_language_from_session(request)\n # Loop over instances\n results = []\n for instance in instances:\n result = instance_as_translated_dict(instance, depth=depth, language=language)\n results.append(result)\n return results\n\n\ndef get_language_from_session(request):\n \"\"\"\n Description:\n Returns the Language instance used by our user, or \"False\" if none is found\n Args:\n request (HttpRequest): HttpRequest from Django\n Returns:\n Language: The currently used language from our app's Language model\n \"\"\"\n language_name = request.session.get(LANGUAGE_SESSION_KEY, False)\n if language_name:\n try:\n language = Language.objects.get(django_language_name=language_name)\n return language\n except Language.DoesNotExist:\n return False\n return False\n\n\ndef instance_as_translated_dict(instance, depth=True, language=None, request=None):\n \"\"\"\n Description:\n Returns a model instance into a dict containing all of its fields\n Language can be given as an argument, or guess through the user of \"request\"\n With \"depth\" set to True, ForeignKey will also be transformed into sub-dict\n Files and images are replaced by a subdict with 'path', 'url', and 'name' keys\n Meaning you will be able to manipulate the dict in an HTML template much like an instance\n Args:\n instance (Model): An instance from any of your models\n depth (bool, optional): Determines if FK will also be transformed into dicts. Defaults to True.\n language (Language, optional): A Language instance from this app. Defaults to None.\n request (HttpRequest, option): HttpRequest from Django. Defaults to None.\n Returns:\n dict: A dict with all of the instance's fields and values\n \"\"\"\n # Checking arguments\n if language is None and request is None:\n raise TypeError(\"You must provide either 'language' or 'request'\")\n # Get the language from the session\n if language is None:\n language = get_language_from_session(request)\n # Loop over fields\n translated_dict = {}\n fields = instance._meta.get_fields()\n for field in fields:\n value = getattr(instance, field.name, None)\n if value is not None:\n value_type = type(value)\n # Case 1: Get the translation\n if value_type == Item:\n new_value = Translation.objects.get(item=value, language=language).text\n # Case 2: Go to the linked model and repeat the process (unless depth=False)\n elif issubclass(value_type, models.Model):\n if depth:\n new_value = instance_as_translated_dict(value, depth=True, language=language)\n else:\n new_value = value\n # Case 3:\n elif value_type in {ImageFieldFile, FieldFile}:\n if value:\n new_value = {\n \"name\": getattr(value, \"name\", \"\"),\n \"url\": getattr(value, \"url\", \"\"),\n \"path\": getattr(value, \"path\", \"\"),\n }\n else:\n new_value = \"\"\n # Case 4: Keep the value as it is\n else:\n new_value = value\n translated_dict[field.name] = new_value\n return translated_dict\n\n\ndef update_user_language(request, language=None, language_id=None):\n \"\"\"\n Description:\n Updates the user current language following Django guildelines\n This will allow for both \"Django\" frontend translations and \"our app\" database translation\n The new language must be passed either through a Language instance or an ID\n Args:\n request (HttpRequest): Request object from Django, used to get to the session\n language (Language, optional): A Language instance from this app. Defaults to None.\n language_id (id, optional): ID of the language in our database. Defaults to None.\n \"\"\"\n # Checking arguments\n if language is None and language_id is None:\n raise TypeError(\"You must provide either 'language' or 'language_id'\")\n # Get the language from the session\n if language is None:\n language = Language.objects.get(id=language_id)\n # Update the user's language\n activate(language.django_language_name)\n request.session[LANGUAGE_SESSION_KEY] = language.django_language_name\n\n```\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://github.com/Jordan-Kowal/django_database_translation/archive/v1.1.2.tar.gz", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Jordan-Kowal/django_database_translation", "keywords": "django,database,db,translation,translate,backend", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-database-translation", "package_url": "https://pypi.org/project/django-database-translation/", "platform": "", "project_url": "https://pypi.org/project/django-database-translation/", "project_urls": { "Download": "https://github.com/Jordan-Kowal/django_database_translation/archive/v1.1.2.tar.gz", "Homepage": "https://github.com/Jordan-Kowal/django_database_translation" }, "release_url": "https://pypi.org/project/django-database-translation/1.1.2/", "requires_dist": null, "requires_python": "", "summary": "Package to handle database translation in your Django apps", "version": "1.1.2" }, "last_serial": 5982589, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "da226d0693715fe3a046c442377b4d40", "sha256": "c84f98f98a249d9dd045ea3f630b2974cd023f1abf7db7a8f75dd0922010b748" }, "downloads": -1, "filename": "django_database_translation-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "da226d0693715fe3a046c442377b4d40", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 18912, "upload_time": "2019-10-14T13:23:20", "url": "https://files.pythonhosted.org/packages/2a/9f/d660da0f19a2eb4042ada813bb8e577ed108a7501d6a76925a9e32fe2754/django_database_translation-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cd3bd8515a0ec8c9e302831e409a34c9", "sha256": "8ba02f1f6e228bb80df09419ec12a6dceaede3fa01cc89e062b1e305f4036101" }, "downloads": -1, "filename": "django_database_translation-1.0.0.tar.gz", "has_sig": false, "md5_digest": "cd3bd8515a0ec8c9e302831e409a34c9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17770, "upload_time": "2019-10-14T13:23:23", "url": "https://files.pythonhosted.org/packages/f9/82/022f2f60e40237ab1f09d98443d6e799222f260eb124d14bf562bd670e4f/django_database_translation-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "6b4ea65f2be3bbebab3ea26738536272", "sha256": "0dc5f88df2a38e70ecc36d3f820d0147f0cb32d37683a2509cfe6cf67277598a" }, "downloads": -1, "filename": "django_database_translation-1.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "6b4ea65f2be3bbebab3ea26738536272", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 21574, "upload_time": "2019-10-16T10:25:17", "url": "https://files.pythonhosted.org/packages/8d/18/1d2ed2ab8d49251db25a90b25ca9438f4d08bc8487ec41f429145d40c62b/django_database_translation-1.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "26aa9ef65514103af0eed7f087a3871f", "sha256": "0e12696a841dc3b5411935c9801617014a9affc7194199af6a152c2e58e8ff86" }, "downloads": -1, "filename": "django_database_translation-1.1.0.tar.gz", "has_sig": false, "md5_digest": "26aa9ef65514103af0eed7f087a3871f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22961, "upload_time": "2019-10-16T10:25:20", "url": "https://files.pythonhosted.org/packages/12/7f/0ca6885adf87acb32ff6700e04e90edfe4ee18f14b8bce1164429d13c2a7/django_database_translation-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "252c72e64292cc2e7de034f5d78761e6", "sha256": "eed36921403a12bbd6c93e4cc6d6cb9043024f4e919215704ea4439ea2e2f854" }, "downloads": -1, "filename": "django_database_translation-1.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "252c72e64292cc2e7de034f5d78761e6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 21575, "upload_time": "2019-10-16T10:48:35", "url": "https://files.pythonhosted.org/packages/9b/55/a65720c2dd23846a3431ab64ed8b72aec862b7c3c1f8450176a4e821ffcc/django_database_translation-1.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "60fdae33fc7e1c2932d90fed6632070a", "sha256": "d4a114d5f88470c08d379f808c718fde5f4f7e90c771809dc580c94a3a569b8e" }, "downloads": -1, "filename": "django_database_translation-1.1.1.tar.gz", "has_sig": false, "md5_digest": "60fdae33fc7e1c2932d90fed6632070a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22969, "upload_time": "2019-10-16T10:48:36", "url": "https://files.pythonhosted.org/packages/a5/dd/ca1e2c5909c742d746d4ec8c2a47104e2b93aeb0095ba65b3db874f91282/django_database_translation-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "3ae425fa8d157ff6b649fabf07489433", "sha256": "0c76e312653a9f431c8363699c53405781441a024fe3086a7c0a7727f9524134" }, "downloads": -1, "filename": "django_database_translation-1.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3ae425fa8d157ff6b649fabf07489433", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 21588, "upload_time": "2019-10-16T11:04:54", "url": "https://files.pythonhosted.org/packages/c2/be/8ba6416ef846b6531075e67c91ca10d9952d1b4716b2825335198fbb1e7b/django_database_translation-1.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a3b5d8b8b906d14c4d33347ebec188f4", "sha256": "907243d556227fdf44863d6ad2e2c91e3f6d6af44835684790fd5a6d3b7a09f8" }, "downloads": -1, "filename": "django_database_translation-1.1.2.tar.gz", "has_sig": false, "md5_digest": "a3b5d8b8b906d14c4d33347ebec188f4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22990, "upload_time": "2019-10-16T11:04:55", "url": "https://files.pythonhosted.org/packages/74/9c/3fd862611aac472c32baf98667b9ee5fd34830a64aaf1c60e93cc7e917fb/django_database_translation-1.1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3ae425fa8d157ff6b649fabf07489433", "sha256": "0c76e312653a9f431c8363699c53405781441a024fe3086a7c0a7727f9524134" }, "downloads": -1, "filename": "django_database_translation-1.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3ae425fa8d157ff6b649fabf07489433", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 21588, "upload_time": "2019-10-16T11:04:54", "url": "https://files.pythonhosted.org/packages/c2/be/8ba6416ef846b6531075e67c91ca10d9952d1b4716b2825335198fbb1e7b/django_database_translation-1.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a3b5d8b8b906d14c4d33347ebec188f4", "sha256": "907243d556227fdf44863d6ad2e2c91e3f6d6af44835684790fd5a6d3b7a09f8" }, "downloads": -1, "filename": "django_database_translation-1.1.2.tar.gz", "has_sig": false, "md5_digest": "a3b5d8b8b906d14c4d33347ebec188f4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22990, "upload_time": "2019-10-16T11:04:55", "url": "https://files.pythonhosted.org/packages/74/9c/3fd862611aac472c32baf98667b9ee5fd34830a64aaf1c60e93cc7e917fb/django_database_translation-1.1.2.tar.gz" } ] }