Add integration tests (#20)
* Adding integration tests with inlines * Adding more tests * FIx make file * Setup CI build matrix to work with integration tests * Try again * Fix workflow synctax * Clean up workflow * Format and some lint stuff * Try codecov * yml * More Testing * Minor lint things * Update * Try again for codecov * Updates * Try? * Ignore quotes * Exclude test project * try this? * checkout required * Rename * clean up configs * Fix * Allow to fail * ignores * FInish the integration tests for cache * Fix workflow yml * fix * Try up again * TRy again * Fix * Fix * Fix tests Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>main
parent
4f50c63f7b
commit
ad7409b567
|
|
@ -1,4 +0,0 @@
|
||||||
[run]
|
|
||||||
relative_files = True
|
|
||||||
omit = admin_confirm/tests/*
|
|
||||||
branch = True
|
|
||||||
|
|
@ -14,47 +14,41 @@ on:
|
||||||
- created
|
- created
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: wemake-python-styleguide
|
||||||
|
uses: wemake-services/wemake-python-styleguide@0.15.2
|
||||||
|
with:
|
||||||
|
path: admin_confirm
|
||||||
|
reporter: 'github-pr-review'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||||
django-version: [2.2, 3.0]
|
django-version: [2.2, 3.0]
|
||||||
|
env:
|
||||||
|
DJANGO_VERSION: ${{ matrix.django-version }}
|
||||||
|
PYTHON_VERSION: ${{ matrix.python-version }}
|
||||||
|
COMPOSE_INTERACTIVE_NO_CLI: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Build Docker for Python 3.6
|
||||||
uses: actions/setup-python@v2
|
if: ${{ matrix.python-version == 3.6 }}
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- name: Install Django ${{ matrix.django-version }}
|
|
||||||
run: |
|
run: |
|
||||||
pip install django==${{ matrix.django-version }}
|
export SELENIUM_VERSION=3.141.0
|
||||||
- name: Install dependencies
|
docker-compose build
|
||||||
|
- name: Build Docker for other Python versions
|
||||||
|
if: ${{ matrix.python-version != 3.6 }}
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
export SELENIUM_VERSION=4.0.0a7
|
||||||
pip install flake8 pytest
|
docker-compose build
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
||||||
- name: Lint with flake8
|
|
||||||
run: |
|
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
|
||||||
- name: Unit Test
|
|
||||||
run: |
|
|
||||||
make test
|
|
||||||
- name: Coveralls
|
|
||||||
uses: AndreMiras/coveralls-python-action@develop
|
|
||||||
with:
|
|
||||||
parallel: true
|
|
||||||
flag-name: Unit Test
|
|
||||||
|
|
||||||
integration-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Build Docker
|
|
||||||
run: docker-compose build
|
|
||||||
- name: Start Docker
|
- name: Start Docker
|
||||||
run: docker-compose up -d
|
run: docker-compose up -d
|
||||||
- name: Integration Test
|
- name: Integration Test
|
||||||
|
|
@ -63,10 +57,9 @@ jobs:
|
||||||
uses: AndreMiras/coveralls-python-action@develop
|
uses: AndreMiras/coveralls-python-action@develop
|
||||||
with:
|
with:
|
||||||
parallel: true
|
parallel: true
|
||||||
flag-name: Integration Test
|
|
||||||
|
|
||||||
coveralls:
|
coveralls:
|
||||||
needs: [test, integration-test]
|
needs: [test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Coveralls Finished
|
- name: Coveralls Finished
|
||||||
|
|
|
||||||
11
Dockerfile
11
Dockerfile
|
|
@ -1,7 +1,12 @@
|
||||||
FROM python:3
|
ARG PYTHON_VERSION=3.8
|
||||||
|
FROM python:${PYTHON_VERSION}
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
ENV USE_DOCKER=true
|
ENV USE_DOCKER=true
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
COPY requirements.txt /code/
|
|
||||||
RUN pip install -r requirements.txt
|
|
||||||
COPY . /code/
|
COPY . /code/
|
||||||
|
ARG DJANGO_VERSION="3.1.7"
|
||||||
|
RUN pip install django==${DJANGO_VERSION}
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
RUN pip install -e .
|
||||||
|
ARG SELENIUM_VERSION="4.0.0a7"
|
||||||
|
RUN pip install selenium~=${SELENIUM_VERSION}
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,13 @@ pyenv vituralenv 3.8.0 django-admin-confirm-3.8.0
|
||||||
|
|
||||||
Now your terminal should have `(django-admin-confirm-3.8.0)` prefix, because `.python-version` should have auto switch your virtual env
|
Now your terminal should have `(django-admin-confirm-3.8.0)` prefix, because `.python-version` should have auto switch your virtual env
|
||||||
|
|
||||||
|
Install requirements
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
Run **migrations** and create a superuser and run the server
|
Run **migrations** and create a superuser and run the server
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -237,7 +244,6 @@ pip uninstall django_admin_confirm
|
||||||
make install-testpypi VERSION=<VERSION>
|
make install-testpypi VERSION=<VERSION>
|
||||||
```
|
```
|
||||||
|
|
||||||
Update version in `requirements.txt`
|
|
||||||
Add test locally
|
Add test locally
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ from django.forms import ModelForm
|
||||||
from admin_confirm.utils import get_admin_change_url, snake_to_title_case
|
from admin_confirm.utils import get_admin_change_url, snake_to_title_case
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
from django.forms.formsets import all_valid
|
|
||||||
from admin_confirm.constants import (
|
from admin_confirm.constants import (
|
||||||
CACHE_TIMEOUT,
|
CACHE_TIMEOUT,
|
||||||
CONFIRMATION_RECEIVED,
|
CONFIRMATION_RECEIVED,
|
||||||
|
|
@ -139,7 +138,7 @@ class AdminConfirmMixin:
|
||||||
return [initial_value, new_value]
|
return [initial_value, new_value]
|
||||||
|
|
||||||
if initial_value:
|
if initial_value:
|
||||||
if new_value == False:
|
if new_value is False:
|
||||||
# Clear has been selected
|
# Clear has been selected
|
||||||
return [initial_value.name, None]
|
return [initial_value.name, None]
|
||||||
elif new_value:
|
elif new_value:
|
||||||
|
|
@ -249,7 +248,7 @@ class AdminConfirmMixin:
|
||||||
if CONFIRM_CHANGE in modified_post:
|
if CONFIRM_CHANGE in modified_post:
|
||||||
del modified_post[CONFIRM_CHANGE]
|
del modified_post[CONFIRM_CHANGE]
|
||||||
|
|
||||||
if object_id and not SAVE_AS_NEW in request.POST:
|
if object_id and SAVE_AS_NEW not in request.POST:
|
||||||
# Update the obj with the new uploaded files
|
# Update the obj with the new uploaded files
|
||||||
# then pass rest of changes to Django
|
# then pass rest of changes to Django
|
||||||
obj = self.model.objects.filter(id=object_id).first()
|
obj = self.model.objects.filter(id=object_id).first()
|
||||||
|
|
@ -287,6 +286,20 @@ class AdminConfirmMixin:
|
||||||
cache.delete_many(CACHE_KEYS.values())
|
cache.delete_many(CACHE_KEYS.values())
|
||||||
return super()._changeform_view(request, object_id, form_url, extra_context)
|
return super()._changeform_view(request, object_id, form_url, extra_context)
|
||||||
|
|
||||||
|
def _get_cleared_fields(self, request):
|
||||||
|
"""
|
||||||
|
Checks for any ImageField or FileField which have been cleared by user.
|
||||||
|
|
||||||
|
Because the form that is generated by Django for the model, would not have the
|
||||||
|
`<field>-clear` inputs in them, they have to be injected into the hidden form
|
||||||
|
on the confirmation page.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
input_name.split("-clear")[0]
|
||||||
|
for input_name in request.POST.keys()
|
||||||
|
if input_name.endswith("-clear")
|
||||||
|
]
|
||||||
|
|
||||||
def _change_confirmation_view(self, request, object_id, form_url, extra_context):
|
def _change_confirmation_view(self, request, object_id, form_url, extra_context):
|
||||||
# This code is taken from super()._changeform_view
|
# This code is taken from super()._changeform_view
|
||||||
# https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1575-L1592
|
# https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1575-L1592
|
||||||
|
|
@ -348,10 +361,14 @@ class AdminConfirmMixin:
|
||||||
save_action = key
|
save_action = key
|
||||||
break
|
break
|
||||||
|
|
||||||
|
cleared_fields = []
|
||||||
if form.is_multipart():
|
if form.is_multipart():
|
||||||
cache.set(CACHE_KEYS["post"], request.POST, timeout=CACHE_TIMEOUT)
|
cache.set(CACHE_KEYS["post"], request.POST, timeout=CACHE_TIMEOUT)
|
||||||
cache.set(CACHE_KEYS["object"], new_object, timeout=CACHE_TIMEOUT)
|
cache.set(CACHE_KEYS["object"], new_object, timeout=CACHE_TIMEOUT)
|
||||||
|
|
||||||
|
# Handle when files are cleared - since the `form` object would not hold that info
|
||||||
|
cleared_fields = self._get_cleared_fields(request)
|
||||||
|
|
||||||
title_action = _("adding") if add_or_new else _("changing")
|
title_action = _("adding") if add_or_new else _("changing")
|
||||||
context = {
|
context = {
|
||||||
**self.admin_site.each_context(request),
|
**self.admin_site.each_context(request),
|
||||||
|
|
@ -368,6 +385,7 @@ class AdminConfirmMixin:
|
||||||
"save_as_new": SAVE_AS_NEW in request.POST,
|
"save_as_new": SAVE_AS_NEW in request.POST,
|
||||||
"submit_name": save_action,
|
"submit_name": save_action,
|
||||||
"form": form,
|
"form": form,
|
||||||
|
"cleared_fields": cleared_fields,
|
||||||
"formsets": formsets,
|
"formsets": formsets,
|
||||||
**(extra_context or {}),
|
**(extra_context or {}),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,11 @@
|
||||||
{% include "admin/change_data.html" %}
|
{% include "admin/change_data.html" %}
|
||||||
|
|
||||||
<form {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} method="post" {% if add %}action="{% url opts|admin_urlname:'add'%}" {% else %}action="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}"{% endif %}>{% csrf_token %}
|
<form {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} method="post" {% if add %}action="{% url opts|admin_urlname:'add'%}" {% else %}action="{% url opts|admin_urlname:'change' object_id|admin_urlquote %}"{% endif %}>{% csrf_token %}
|
||||||
<div class="hidden">
|
<div class="hidden" id="hidden-form">
|
||||||
{{form.as_p}}
|
{{form.as_p}}
|
||||||
|
{% for cleared_field in cleared_fields %}
|
||||||
|
<input type="checkbox" name="{{ cleared_field }}-clear" checked>
|
||||||
|
{% endfor %}
|
||||||
{% for formset in formsets %}
|
{% for formset in formsets %}
|
||||||
{{ formset.as_p }}
|
{{ formset.as_p }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
|
@ -16,5 +15,5 @@ def format_change_data_field_value(field_value):
|
||||||
output += "<li>" + escape(value) + "</li>"
|
output += "<li>" + escape(value) + "</li>"
|
||||||
output += "</ul>"
|
output += "</ul>"
|
||||||
return mark_safe(output)
|
return mark_safe(output)
|
||||||
except:
|
except Exception:
|
||||||
return field_value
|
return field_value
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ You seem concerned about the stability and reliability of this package. You're p
|
||||||
|
|
||||||
So if you want to include this package in your production codebase, be aware that AdminConfirmMixin works best with simple unmodified ModelAdmins.
|
So if you want to include this package in your production codebase, be aware that AdminConfirmMixin works best with simple unmodified ModelAdmins.
|
||||||
|
|
||||||
|
# Probable Issues
|
||||||
|
|
||||||
|
These are some areas which might/probably have issues that are not currently tested. Use at your own risk!
|
||||||
|
|
||||||
|
- [ ] Saving file/image changes on inlines when confirming change on parent model
|
||||||
|
|
||||||
## Save Options
|
## Save Options
|
||||||
|
|
||||||
- [x] Save
|
- [x] Save
|
||||||
|
|
@ -71,10 +77,10 @@ Confirmation on inline changes is not a current feature of this project.
|
||||||
|
|
||||||
Confirmation on add/change of ModelAdmin that includes inlines needs to be tested. Use AdminConfirmMixin with ModelAdmin containing inlines at your own risk.
|
Confirmation on add/change of ModelAdmin that includes inlines needs to be tested. Use AdminConfirmMixin with ModelAdmin containing inlines at your own risk.
|
||||||
|
|
||||||
- [ ] .inlines
|
- [x] .inlines
|
||||||
- [ ] .get_inline_instances()
|
- [x] .get_inline_instances()
|
||||||
- [ ] .get_inlines() (New in Django 3.0)
|
- [x] .get_inlines() (New in Django 3.0)
|
||||||
- [ ] .get_formsets_with_inlines()
|
- [ ] .get_formsets_with_inlines() ???
|
||||||
|
|
||||||
#### Options for inlines
|
#### Options for inlines
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
|
import socket
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import LiveServerTestCase
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||||
|
|
||||||
|
|
||||||
class AdminConfirmTestCase(TestCase):
|
class AdminConfirmTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
@ -62,16 +68,10 @@ class AdminConfirmTestCase(TestCase):
|
||||||
self.assertIn("apple", rendered_content)
|
self.assertIn("apple", rendered_content)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import socket
|
|
||||||
from django.test import LiveServerTestCase
|
|
||||||
from selenium import webdriver
|
|
||||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
|
||||||
|
|
||||||
|
|
||||||
class AdminConfirmIntegrationTestCase(LiveServerTestCase):
|
class AdminConfirmIntegrationTestCase(LiveServerTestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
|
||||||
cls.host = socket.gethostbyname(socket.gethostname())
|
cls.host = socket.gethostbyname(socket.gethostname())
|
||||||
cls.selenium = webdriver.Remote(
|
cls.selenium = webdriver.Remote(
|
||||||
command_executor="http://selenium:4444/wd/hub",
|
command_executor="http://selenium:4444/wd/hub",
|
||||||
|
|
@ -79,6 +79,25 @@ class AdminConfirmIntegrationTestCase(LiveServerTestCase):
|
||||||
)
|
)
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.superuser = User.objects.create_superuser(
|
||||||
|
username="super", email="super@email.org", password="pass"
|
||||||
|
)
|
||||||
|
self.client.force_login(self.superuser)
|
||||||
|
|
||||||
|
cookie = self.client.cookies["sessionid"]
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + "/admin/"
|
||||||
|
) # selenium will set cookie domain based on current page domain
|
||||||
|
self.selenium.add_cookie(
|
||||||
|
{"name": "sessionid", "value": cookie.value, "secure": False, "path": "/"}
|
||||||
|
)
|
||||||
|
return super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
cache.clear()
|
||||||
|
return super().tearDown()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
cls.selenium.quit()
|
cls.selenium.quit()
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,6 @@ from admin_confirm.tests.helpers import AdminConfirmIntegrationTestCase
|
||||||
|
|
||||||
class SmokeTest(AdminConfirmIntegrationTestCase):
|
class SmokeTest(AdminConfirmIntegrationTestCase):
|
||||||
def test_load_admin(self):
|
def test_load_admin(self):
|
||||||
self.selenium.get(self.live_server_url+'/admin/')
|
self.selenium.get(self.live_server_url + "/admin/")
|
||||||
self.assertIn('Django', self.selenium.title)
|
self.assertIn("Django", self.selenium.title)
|
||||||
|
self.assertIn("Market", self.selenium.page_source)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
"""
|
||||||
|
Tests confirmation of add/change
|
||||||
|
on ModelAdmin that utilize caches
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
from importlib import reload
|
||||||
|
from tests.factories import ShopFactory
|
||||||
|
from tests.market.models import GeneralManager, Item, ShoppingMall, Town
|
||||||
|
|
||||||
|
from admin_confirm.tests.helpers import AdminConfirmIntegrationTestCase
|
||||||
|
from tests.market.admin import shoppingmall_admin
|
||||||
|
|
||||||
|
from admin_confirm.constants import CONFIRM_CHANGE
|
||||||
|
from selenium.webdriver.support.ui import Select
|
||||||
|
from selenium.common.exceptions import NoSuchElementException
|
||||||
|
from selenium.webdriver.remote.file_detector import LocalFileDetector
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmWithInlinesTests(AdminConfirmIntegrationTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.selenium.file_detector = LocalFileDetector()
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
reload(shoppingmall_admin)
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_models_without_files_should_not_have_confirmation_received(self):
|
||||||
|
mall = ShoppingMall.objects.create(name="mall")
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
# Should ask for confirmation of change
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Change name
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated name
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
name = hidden_form.find_element_by_name("name")
|
||||||
|
self.assertIn("New Name", name.get_attribute("value"))
|
||||||
|
|
||||||
|
with self.assertRaises(NoSuchElementException):
|
||||||
|
self.selenium.find_element_by_name("_confirmation_received")
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should persist change
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
|
||||||
|
def test_models_with_files_should_have_confirmation_received(self):
|
||||||
|
item = Item.objects.create(name="item", price=1)
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/item/{item.id}/change/"
|
||||||
|
)
|
||||||
|
# Should ask for confirmation of change
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Change price
|
||||||
|
price = self.selenium.find_element_by_name("price")
|
||||||
|
price.send_keys(2)
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated price
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
price = hidden_form.find_element_by_name("price")
|
||||||
|
self.assertEqual("21.00", price.get_attribute("value"))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_confirmation_received")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
item.refresh_from_db()
|
||||||
|
|
||||||
|
def test_should_save_file_additions(self):
|
||||||
|
selenium_version = pkg_resources.get_distribution("selenium").parsed_version
|
||||||
|
if selenium_version.major < 4:
|
||||||
|
pytest.skip(
|
||||||
|
"Known issue `https://github.com/SeleniumHQ/selenium/issues/8762` with this selenium version."
|
||||||
|
)
|
||||||
|
|
||||||
|
item = Item.objects.create(
|
||||||
|
name="item", price=1, currency=Item.VALID_CURRENCIES[0][0]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/item/{item.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
price = self.selenium.find_element_by_name("price")
|
||||||
|
price.send_keys(2)
|
||||||
|
|
||||||
|
# Upload a new file
|
||||||
|
self.selenium.find_element_by_id("id_file").send_keys(
|
||||||
|
os.getcwd() + "/screenshot.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated price
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
price = hidden_form.find_element_by_name("price")
|
||||||
|
self.assertEqual("21.00", price.get_attribute("value"))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_confirmation_received")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
item.refresh_from_db()
|
||||||
|
self.assertEqual(21, int(item.price))
|
||||||
|
self.assertIn("screenshot.png", item.file.name)
|
||||||
|
|
||||||
|
def test_should_save_file_changes(self):
|
||||||
|
selenium_version = pkg_resources.get_distribution("selenium").parsed_version
|
||||||
|
if selenium_version.major < 4:
|
||||||
|
pytest.skip(
|
||||||
|
"Known issue `https://github.com/SeleniumHQ/selenium/issues/8762` with this selenium version."
|
||||||
|
)
|
||||||
|
|
||||||
|
file = SimpleUploadedFile(
|
||||||
|
name="old_file.jpg",
|
||||||
|
content=open("screenshot.png", "rb").read(),
|
||||||
|
content_type="image/jpeg",
|
||||||
|
)
|
||||||
|
item = Item.objects.create(
|
||||||
|
name="item", price=1, currency=Item.VALID_CURRENCIES[0][0], file=file
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/item/{item.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
price = self.selenium.find_element_by_name("price")
|
||||||
|
price.send_keys(2)
|
||||||
|
|
||||||
|
# Upload a new file
|
||||||
|
self.selenium.find_element_by_id("id_file").send_keys(
|
||||||
|
os.getcwd() + "/screenshot.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated price
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
price = hidden_form.find_element_by_name("price")
|
||||||
|
self.assertEqual("21.00", price.get_attribute("value"))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_confirmation_received")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
item.refresh_from_db()
|
||||||
|
self.assertEqual(21, int(item.price))
|
||||||
|
self.assertIn("screenshot.png", item.file.name)
|
||||||
|
|
||||||
|
def test_should_remove_file_if_clear_selected(self):
|
||||||
|
file = SimpleUploadedFile(
|
||||||
|
name="old_file.jpg",
|
||||||
|
content=open("screenshot.png", "rb").read(),
|
||||||
|
content_type="image/jpeg",
|
||||||
|
)
|
||||||
|
item = Item.objects.create(
|
||||||
|
name="item", price=1, currency=Item.VALID_CURRENCIES[0][0], file=file
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/item/{item.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
price = self.selenium.find_element_by_name("price")
|
||||||
|
price.send_keys(2)
|
||||||
|
|
||||||
|
# Choose to clear the existing file
|
||||||
|
self.selenium.find_element_by_id("file-clear_id").click()
|
||||||
|
self.assertTrue(
|
||||||
|
self.selenium.find_element_by_xpath(
|
||||||
|
".//*[@id='file-clear_id']"
|
||||||
|
).get_attribute("checked")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated price
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
price = hidden_form.find_element_by_name("price")
|
||||||
|
self.assertEqual("21.00", price.get_attribute("value"))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_confirmation_received")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
item.refresh_from_db()
|
||||||
|
self.assertEqual(21, int(item.price))
|
||||||
|
# Should have cleared `file` since clear was selected
|
||||||
|
self.assertFalse(item.file)
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
"""
|
||||||
|
Tests confirmation of add/change
|
||||||
|
on ModelAdmin that includes inlines
|
||||||
|
|
||||||
|
Does not test confirmation of inline changes
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import pkg_resources
|
||||||
|
from importlib import reload
|
||||||
|
from tests.factories import ShopFactory
|
||||||
|
from tests.market.models import GeneralManager, ShoppingMall, Town
|
||||||
|
|
||||||
|
from admin_confirm.tests.helpers import AdminConfirmIntegrationTestCase
|
||||||
|
from tests.market.admin import shoppingmall_admin
|
||||||
|
|
||||||
|
from admin_confirm.constants import CONFIRM_CHANGE
|
||||||
|
from selenium.webdriver.support.ui import Select
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmWithInlinesTests(AdminConfirmIntegrationTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.admin = shoppingmall_admin.ShoppingMallAdmin
|
||||||
|
self.admin.inlines = [shoppingmall_admin.ShopInline]
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
reload(shoppingmall_admin)
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_should_have_hidden_form(self):
|
||||||
|
mall = ShoppingMall.objects.create(name="mall")
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
# Should ask for confirmation of change
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Change name
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should have hidden form containing the updated name
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
name = hidden_form.find_element_by_name("name")
|
||||||
|
self.assertIn("New Name", name.get_attribute("value"))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
# Should persist change
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
|
||||||
|
def test_should_have_hidden_formsets(self):
|
||||||
|
# Not having formsets would cause a `ManagementForm tampered with` issue
|
||||||
|
gm = GeneralManager.objects.create(name="gm")
|
||||||
|
shops = [ShopFactory(name=i) for i in range(3)]
|
||||||
|
town = Town.objects.create(name="town")
|
||||||
|
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||||
|
mall.shops.set(shops)
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
hidden_form.find_element_by_name("ShoppingMall_shops-TOTAL_FORMS")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
|
||||||
|
def test_should_have_saved_inline_changes(self):
|
||||||
|
gm = GeneralManager.objects.create(name="gm")
|
||||||
|
town = Town.objects.create(name="town")
|
||||||
|
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||||
|
|
||||||
|
shops = [ShopFactory(name=i) for i in range(3)]
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
# Change shops via inline form
|
||||||
|
select_shop = Select(
|
||||||
|
self.selenium.find_element_by_name("ShoppingMall_shops-0-shop")
|
||||||
|
)
|
||||||
|
select_shop.select_by_value(str(shops[2].id))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
hidden_form.find_element_by_name("ShoppingMall_shops-TOTAL_FORMS")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
self.assertIn(shops[2], mall.shops.all())
|
||||||
|
|
||||||
|
def test_should_respect_get_inlines(self):
|
||||||
|
# New in Django 3.0
|
||||||
|
django_version = pkg_resources.get_distribution("Django").parsed_version
|
||||||
|
if django_version.major < 3:
|
||||||
|
pytest.skip(
|
||||||
|
"get_inlines() introducted in Django 3.0, and is not in this version"
|
||||||
|
)
|
||||||
|
|
||||||
|
shoppingmall_admin.ShoppingMallAdmin.inlines = []
|
||||||
|
shoppingmall_admin.ShoppingMallAdmin.get_inlines = (
|
||||||
|
lambda self, request, obj=None: [shoppingmall_admin.ShopInline]
|
||||||
|
)
|
||||||
|
|
||||||
|
gm = GeneralManager.objects.create(name="gm")
|
||||||
|
town = Town.objects.create(name="town")
|
||||||
|
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||||
|
|
||||||
|
shops = [ShopFactory(name=i) for i in range(3)]
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
# Change shops via inline form
|
||||||
|
select_shop = Select(
|
||||||
|
self.selenium.find_element_by_name("ShoppingMall_shops-0-shop")
|
||||||
|
)
|
||||||
|
select_shop.select_by_value(str(shops[2].id))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
hidden_form.find_element_by_name("ShoppingMall_shops-TOTAL_FORMS")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
self.assertIn(shops[2], mall.shops.all())
|
||||||
|
|
||||||
|
def test_should_respect_get_inline_instances(self):
|
||||||
|
shoppingmall_admin.ShoppingMallAdmin.inlines = []
|
||||||
|
shoppingmall_admin.ShoppingMallAdmin.get_inline_instances = (
|
||||||
|
lambda self, request, obj=None: shoppingmall_admin.ShopInline(
|
||||||
|
self.model, self.admin_site
|
||||||
|
)
|
||||||
|
)
|
||||||
|
gm = GeneralManager.objects.create(name="gm")
|
||||||
|
town = Town.objects.create(name="town")
|
||||||
|
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||||
|
|
||||||
|
shops = [ShopFactory(name=i) for i in range(3)]
|
||||||
|
|
||||||
|
self.selenium.get(
|
||||||
|
self.live_server_url + f"/admin/market/shoppingmall/{mall.id}/change/"
|
||||||
|
)
|
||||||
|
self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)
|
||||||
|
|
||||||
|
# Make a change to trigger confirmation page
|
||||||
|
name = self.selenium.find_element_by_name("name")
|
||||||
|
name.send_keys("New Name")
|
||||||
|
|
||||||
|
# Change shops via inline form
|
||||||
|
select_shop = Select(
|
||||||
|
self.selenium.find_element_by_name("ShoppingMall_shops-0-shop")
|
||||||
|
)
|
||||||
|
select_shop.select_by_value(str(shops[2].id))
|
||||||
|
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
self.assertIn("Confirm", self.selenium.page_source)
|
||||||
|
|
||||||
|
hidden_form = self.selenium.find_element_by_id("hidden-form")
|
||||||
|
hidden_form.find_element_by_name("ShoppingMall_shops-TOTAL_FORMS")
|
||||||
|
self.selenium.find_element_by_name("_continue").click()
|
||||||
|
|
||||||
|
mall.refresh_from_db()
|
||||||
|
self.assertIn("New Name", mall.name)
|
||||||
|
self.assertIn(shops[2], mall.shops.all())
|
||||||
|
|
@ -21,8 +21,6 @@ class TestAdminOptions(AdminConfirmTestCase):
|
||||||
mall.shops.set(shops)
|
mall.shops.set(shops)
|
||||||
|
|
||||||
# new values
|
# new values
|
||||||
gm2 = GeneralManager.objects.create(name="gm2")
|
|
||||||
shops2 = [ShopFactory() for i in range(3)]
|
|
||||||
town2 = Town.objects.create(name="town2")
|
town2 = Town.objects.create(name="town2")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -92,7 +90,6 @@ class TestAdminOptions(AdminConfirmTestCase):
|
||||||
|
|
||||||
# new values
|
# new values
|
||||||
gm2 = GeneralManager.objects.create(name="gm2")
|
gm2 = GeneralManager.objects.create(name="gm2")
|
||||||
shops2 = [ShopFactory() for i in range(3)]
|
|
||||||
town2 = Town.objects.create(name="town2")
|
town2 = Town.objects.create(name="town2")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -164,7 +161,6 @@ class TestAdminOptions(AdminConfirmTestCase):
|
||||||
|
|
||||||
# new values
|
# new values
|
||||||
gm2 = GeneralManager.objects.create(name="gm2")
|
gm2 = GeneralManager.objects.create(name="gm2")
|
||||||
shops2 = [ShopFactory() for i in range(3)]
|
|
||||||
town2 = Town.objects.create(name="town2")
|
town2 = Town.objects.create(name="town2")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -205,7 +201,6 @@ class TestAdminOptions(AdminConfirmTestCase):
|
||||||
|
|
||||||
# new values
|
# new values
|
||||||
gm2 = GeneralManager.objects.create(name="gm2")
|
gm2 = GeneralManager.objects.create(name="gm2")
|
||||||
shops2 = [ShopFactory() for i in range(3)]
|
|
||||||
town2 = Town.objects.create(name="town2")
|
town2 = Town.objects.create(name="town2")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
@ -246,7 +241,6 @@ class TestAdminOptions(AdminConfirmTestCase):
|
||||||
|
|
||||||
# new values
|
# new values
|
||||||
gm2 = GeneralManager.objects.create(name="gm2")
|
gm2 = GeneralManager.objects.create(name="gm2")
|
||||||
shops2 = [ShopFactory() for i in range(3)]
|
|
||||||
town2 = Town.objects.create(name="town2")
|
town2 = Town.objects.create(name="town2")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from admin_confirm.admin import AdminConfirmMixin
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
item.refresh_from_db()
|
item.refresh_from_db()
|
||||||
|
|
@ -290,7 +290,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
data[CONFIRMATION_RECEIVED] = True
|
data[CONFIRMATION_RECEIVED] = True
|
||||||
|
|
||||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||||
response = self.client.post(f"/admin/market/item/add/", data=data)
|
response = self.client.post("/admin/market/item/add/", data=data)
|
||||||
# Should show message to user with correct obj and path
|
# Should show message to user with correct obj and path
|
||||||
message_user.assert_called_once()
|
message_user.assert_called_once()
|
||||||
message = message_user.call_args[0][1]
|
message = message_user.call_args[0][1]
|
||||||
|
|
@ -299,7 +299,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should not have redirected to changelist
|
# Should not have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
self.item.refresh_from_db()
|
self.item.refresh_from_db()
|
||||||
|
|
@ -353,7 +353,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
data[CONFIRMATION_RECEIVED] = True
|
data[CONFIRMATION_RECEIVED] = True
|
||||||
|
|
||||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||||
response = self.client.post(f"/admin/market/item/add/", data=data)
|
response = self.client.post("/admin/market/item/add/", data=data)
|
||||||
# Should show message to user with correct obj and path
|
# Should show message to user with correct obj and path
|
||||||
message_user.assert_called_once()
|
message_user.assert_called_once()
|
||||||
message = message_user.call_args[0][1]
|
message = message_user.call_args[0][1]
|
||||||
|
|
@ -362,7 +362,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should not have redirected to changelist
|
# Should not have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
self.item.refresh_from_db()
|
self.item.refresh_from_db()
|
||||||
|
|
@ -398,17 +398,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
"_save": True,
|
"_save": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Upload new file
|
|
||||||
f2 = SimpleUploadedFile(
|
|
||||||
name="test_file2.jpg",
|
|
||||||
content=open(self.image_path, "rb").read(),
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
# Set cache
|
|
||||||
cache_item = Item(
|
|
||||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure there's no post cached obj
|
# Make sure there's no post cached obj
|
||||||
cache.delete(CACHE_KEYS["object"])
|
cache.delete(CACHE_KEYS["object"])
|
||||||
cache.set(CACHE_KEYS["post"], data)
|
cache.set(CACHE_KEYS["post"], data)
|
||||||
|
|
@ -418,7 +407,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
data[CONFIRMATION_RECEIVED] = True
|
data[CONFIRMATION_RECEIVED] = True
|
||||||
|
|
||||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||||
response = self.client.post(f"/admin/market/item/add/", data=data)
|
response = self.client.post("/admin/market/item/add/", data=data)
|
||||||
# Should show message to user with correct obj and path
|
# Should show message to user with correct obj and path
|
||||||
message_user.assert_called_once()
|
message_user.assert_called_once()
|
||||||
message = message_user.call_args[0][1]
|
message = message_user.call_args[0][1]
|
||||||
|
|
@ -427,7 +416,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should not have redirected to changelist
|
# Should not have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
self.item.refresh_from_db()
|
self.item.refresh_from_db()
|
||||||
|
|
@ -463,17 +452,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
"_save": True,
|
"_save": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Upload new file
|
|
||||||
f2 = SimpleUploadedFile(
|
|
||||||
name="test_file2.jpg",
|
|
||||||
content=open(self.image_path, "rb").read(),
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
# Set cache
|
|
||||||
cache_item = Item(
|
|
||||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make sure there's no cache
|
# Make sure there's no cache
|
||||||
cache.delete(CACHE_KEYS["object"])
|
cache.delete(CACHE_KEYS["object"])
|
||||||
cache.delete(CACHE_KEYS["post"])
|
cache.delete(CACHE_KEYS["post"])
|
||||||
|
|
@ -483,7 +461,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
data[CONFIRMATION_RECEIVED] = True
|
data[CONFIRMATION_RECEIVED] = True
|
||||||
|
|
||||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||||
response = self.client.post(f"/admin/market/item/add/", data=data)
|
response = self.client.post("/admin/market/item/add/", data=data)
|
||||||
# Should show message to user with correct obj and path
|
# Should show message to user with correct obj and path
|
||||||
message_user.assert_called_once()
|
message_user.assert_called_once()
|
||||||
message = message_user.call_args[0][1]
|
message = message_user.call_args[0][1]
|
||||||
|
|
@ -492,7 +470,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should not have redirected to changelist
|
# Should not have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
self.item.refresh_from_db()
|
self.item.refresh_from_db()
|
||||||
|
|
@ -571,7 +549,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
item.refresh_from_db()
|
item.refresh_from_db()
|
||||||
|
|
@ -601,12 +579,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
# Load the Change Item Page
|
# Load the Change Item Page
|
||||||
ItemAdmin.save_as_continue = False
|
ItemAdmin.save_as_continue = False
|
||||||
|
|
||||||
# Upload new image and remove file
|
|
||||||
i2 = SimpleUploadedFile(
|
|
||||||
name="test_image2.jpg",
|
|
||||||
content=open(self.image_path, "rb").read(),
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
# Request.POST
|
# Request.POST
|
||||||
data = {
|
data = {
|
||||||
"id": item.id,
|
"id": item.id,
|
||||||
|
|
@ -619,14 +591,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
"_saveasnew": True,
|
"_saveasnew": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set cache
|
|
||||||
cache_item = Item(
|
|
||||||
name=data["name"],
|
|
||||||
price=data["price"],
|
|
||||||
currency=data["currency"],
|
|
||||||
image=i2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure no cached obj
|
# Ensure no cached obj
|
||||||
cache.delete(CACHE_KEYS["object"])
|
cache.delete(CACHE_KEYS["object"])
|
||||||
cache.set(CACHE_KEYS["post"], data)
|
cache.set(CACHE_KEYS["post"], data)
|
||||||
|
|
@ -647,7 +611,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
item.refresh_from_db()
|
item.refresh_from_db()
|
||||||
|
|
@ -676,12 +640,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
# Load the Change Item Page
|
# Load the Change Item Page
|
||||||
ItemAdmin.save_as_continue = False
|
ItemAdmin.save_as_continue = False
|
||||||
|
|
||||||
# Upload new image and remove file
|
|
||||||
i2 = SimpleUploadedFile(
|
|
||||||
name="test_image2.jpg",
|
|
||||||
content=open(self.image_path, "rb").read(),
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
# Request.POST
|
# Request.POST
|
||||||
data = {
|
data = {
|
||||||
"id": item.id,
|
"id": item.id,
|
||||||
|
|
@ -694,14 +652,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
"_saveasnew": True,
|
"_saveasnew": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set cache
|
|
||||||
cache_item = Item(
|
|
||||||
name=data["name"],
|
|
||||||
price=data["price"],
|
|
||||||
currency=data["currency"],
|
|
||||||
image=i2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure no cache
|
# Ensure no cache
|
||||||
cache.delete(CACHE_KEYS["object"])
|
cache.delete(CACHE_KEYS["object"])
|
||||||
cache.delete(CACHE_KEYS["post"])
|
cache.delete(CACHE_KEYS["post"])
|
||||||
|
|
@ -722,7 +672,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should not have changed existing item
|
# Should not have changed existing item
|
||||||
item.refresh_from_db()
|
item.refresh_from_db()
|
||||||
|
|
@ -790,7 +740,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should have changed existing item
|
# Should have changed existing item
|
||||||
self.assertEqual(Item.objects.count(), 1)
|
self.assertEqual(Item.objects.count(), 1)
|
||||||
|
|
@ -890,12 +840,6 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
# Load the Change Item Page
|
# Load the Change Item Page
|
||||||
ItemAdmin.save_as_continue = False
|
ItemAdmin.save_as_continue = False
|
||||||
|
|
||||||
# Upload new image and remove file
|
|
||||||
i2 = SimpleUploadedFile(
|
|
||||||
name="test_image2.jpg",
|
|
||||||
content=open(self.image_path, "rb").read(),
|
|
||||||
content_type="image/jpeg",
|
|
||||||
)
|
|
||||||
# Request.POST
|
# Request.POST
|
||||||
data = {
|
data = {
|
||||||
"id": item.id,
|
"id": item.id,
|
||||||
|
|
@ -930,7 +874,7 @@ class TestFileCache(AdminConfirmTestCase):
|
||||||
self.assertNotIn("You may edit it again below.", message)
|
self.assertNotIn("You may edit it again below.", message)
|
||||||
|
|
||||||
# Should have redirected to changelist
|
# Should have redirected to changelist
|
||||||
self.assertEqual(response.url, f"/admin/market/item/")
|
self.assertEqual(response.url, "/admin/market/item/")
|
||||||
|
|
||||||
# Should have changed existing item
|
# Should have changed existing item
|
||||||
self.assertEqual(Item.objects.count(), 1)
|
self.assertEqual(Item.objects.count(), 1)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@ version: "3.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: .
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
PYTHON_VERSION: "$PYTHON_VERSION"
|
||||||
|
DJANGO_VERSION: "$DJANGO_VERSION"
|
||||||
|
SELENIUM_VERSION: "$SELENIUM_VERSION"
|
||||||
command: python tests/manage.py runserver 0.0.0.0:8000
|
command: python tests/manage.py runserver 0.0.0.0:8000
|
||||||
volumes:
|
volumes:
|
||||||
- .:/code
|
- .:/code
|
||||||
|
|
@ -16,3 +22,5 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "4444:4444" # Selenium
|
- "4444:4444" # Selenium
|
||||||
- "5900:5900" # VNC
|
- "5900:5900" # VNC
|
||||||
|
volumes:
|
||||||
|
- .:/code
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
[pytest]
|
|
||||||
DJANGO_SETTINGS_MODULE=tests.test_project.settings.test
|
|
||||||
addopts = --doctest-modules -ra -l --tb=short --show-capture=stdout --color=yes
|
|
||||||
testpaths = admin_confirm
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
Django>=1.7.0
|
Django>=1.7.0
|
||||||
factory-boy~=3.0.1
|
factory-boy~=3.0.1
|
||||||
django-admin-confirm~=0.2.2
|
|
||||||
coverage~=5.4
|
coverage~=5.4
|
||||||
pytest~=6.2.2
|
pytest~=6.2.2
|
||||||
pytest-django~=4.1.0
|
pytest-django~=4.1.0
|
||||||
|
|
@ -8,4 +7,10 @@ readme-renderer~=28.0
|
||||||
twine~=3.3.0
|
twine~=3.3.0
|
||||||
coveralls~=3.0.0
|
coveralls~=3.0.0
|
||||||
Pillow~=8.1.0 # For ImageField
|
Pillow~=8.1.0 # For ImageField
|
||||||
selenium~=3.141.0
|
|
||||||
|
# Known issue: https://github.com/SeleniumHQ/selenium/issues/8762
|
||||||
|
# Python 3.6 should use because selenium 4 doesn't work with py3.6
|
||||||
|
# selenium~=3.141.0
|
||||||
|
|
||||||
|
# Others should use
|
||||||
|
selenium~=4.0.0.a5
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
[flake8]
|
||||||
|
max-complexity = 10
|
||||||
|
max-line-length = 127
|
||||||
|
paths = admin_confirm
|
||||||
|
exclude =
|
||||||
|
admin_confirm/tests/*
|
||||||
|
tests/*
|
||||||
|
ignore =
|
||||||
|
C812 # missing trailing comma
|
||||||
|
I001 # isort found an import in the wrong position
|
||||||
|
I004 # sisort found an unexpected blank line in imports
|
||||||
|
Q000 # Remove bad quotes
|
||||||
|
WPS110 # Seems to require no one word variable names
|
||||||
|
WPS305 # Found f string
|
||||||
|
WPS336 # Explicit string concatination
|
||||||
|
per-file-ignores =
|
||||||
|
admin_confirm/tests/*: D102, WPS118, WPS204
|
||||||
|
[coverage:run]
|
||||||
|
relative_files = True
|
||||||
|
omit = admin_confirm/tests/*
|
||||||
|
branch = True
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE=tests.test_project.settings.test
|
||||||
|
addopts = --doctest-modules -ra -l --tb=short --show-capture=stdout --color=yes
|
||||||
|
testpaths = admin_confirm
|
||||||
3
setup.py
3
setup.py
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
here = os.path.abspath(os.path.dirname(__file__))
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
@ -8,7 +9,7 @@ setup(
|
||||||
name="django-admin-confirm",
|
name="django-admin-confirm",
|
||||||
version="0.2.3.dev9",
|
version="0.2.3.dev9",
|
||||||
packages=["admin_confirm"],
|
packages=["admin_confirm"],
|
||||||
description="Adds confirmation to Django Admin changes, additions and actions",
|
description=("Adds confirmation to Django Admin changes, additions and actions"),
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
long_description=README,
|
long_description=README,
|
||||||
author="Thu Trang Pham",
|
author="Thu Trang Pham",
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('market', '0003_auto_20201108_1717'),
|
("market", "0003_auto_20201108_1717"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='inventory',
|
model_name="inventory",
|
||||||
name='notes',
|
name="notes",
|
||||||
field=models.TextField(blank=True, default='This is the default', null=True),
|
field=models.TextField(
|
||||||
|
blank=True, default="This is the default", null=True
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,24 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('market', '0004_inventory_notes'),
|
("market", "0004_inventory_notes"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='ShoppingMall',
|
name="ShoppingMall",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=120)),
|
"id",
|
||||||
('shops', models.ManyToManyField(to='market.Shop')),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=120)),
|
||||||
|
("shops", models.ManyToManyField(to="market.Shop")),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -7,42 +7,68 @@ import django.db.models.deletion
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('market', '0005_shoppingmall'),
|
("market", "0005_shoppingmall"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='GeneralManager',
|
name="GeneralManager",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=120)),
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=120)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Town',
|
name="Town",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
(
|
||||||
('name', models.CharField(max_length=120)),
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=120)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='file',
|
name="file",
|
||||||
field=models.FileField(blank=True, null=True, upload_to='tmp/files'),
|
field=models.FileField(blank=True, null=True, upload_to="tmp/files"),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='image',
|
name="image",
|
||||||
field=models.ImageField(blank=True, null=True, upload_to='tmp/items'),
|
field=models.ImageField(blank=True, null=True, upload_to="tmp/items"),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='shoppingmall',
|
model_name="shoppingmall",
|
||||||
name='general_manager',
|
name="general_manager",
|
||||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='market.generalmanager'),
|
field=models.OneToOneField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="market.generalmanager",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='shoppingmall',
|
model_name="shoppingmall",
|
||||||
name='town',
|
name="town",
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='market.town'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="market.town",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('market', '0006_auto_20210222_0312'),
|
("market", "0006_auto_20210222_0312"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='generalmanager',
|
model_name="generalmanager",
|
||||||
name='headshot',
|
name="headshot",
|
||||||
field=models.ImageField(blank=True, null=True, upload_to='tmp/gm/headshots'),
|
field=models.ImageField(
|
||||||
|
blank=True, null=True, upload_to="tmp/gm/headshots"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('market', '0007_generalmanager_headshot'),
|
("market", "0007_generalmanager_headshot"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='item',
|
model_name="item",
|
||||||
name='description',
|
name="description",
|
||||||
field=models.TextField(blank=True, null=True),
|
field=models.TextField(blank=True, null=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.1.7 on 2021-03-04 03:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("market", "0008_item_description"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="shoppingmall",
|
||||||
|
name="shops",
|
||||||
|
field=models.ManyToManyField(blank=True, null=True, to="market.Shop"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -49,7 +49,7 @@ class Town(models.Model):
|
||||||
|
|
||||||
class ShoppingMall(models.Model):
|
class ShoppingMall(models.Model):
|
||||||
name = models.CharField(max_length=120)
|
name = models.CharField(max_length=120)
|
||||||
shops = models.ManyToManyField(Shop)
|
shops = models.ManyToManyField(Shop, blank=True, null=True)
|
||||||
general_manager = models.OneToOneField(
|
general_manager = models.OneToOneField(
|
||||||
GeneralManager, on_delete=models.CASCADE, null=True, blank=True
|
GeneralManager, on_delete=models.CASCADE, null=True, blank=True
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ SECRET_KEY = "=yddl-40388w3e2hl$e8)revce=n67_idi8pfejtn3!+2%!_qt"
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
USE_DCOKER = os.environ.get("USE_DOCKER", '').lower() == "true"
|
USE_DCOKER = os.environ.get("USE_DOCKER", "").lower() == "true"
|
||||||
|
|
||||||
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
|
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
|
||||||
if USE_DCOKER:
|
if USE_DCOKER:
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
ALLOWED_HOSTS = [socket.gethostbyname(socket.gethostname())]
|
ALLOWED_HOSTS += [socket.gethostbyname(socket.gethostname())]
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
INSTALLED_APPS = INSTALLED_APPS + ['market']
|
INSTALLED_APPS = INSTALLED_APPS + ["market"]
|
||||||
WSGI_APPLICATION = "test_project.wsgi.application"
|
WSGI_APPLICATION = "test_project.wsgi.application"
|
||||||
ROOT_URLCONF = "test_project.urls"
|
ROOT_URLCONF = "test_project.urls"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
INSTALLED_APPS = INSTALLED_APPS + ['tests.market']
|
INSTALLED_APPS = INSTALLED_APPS + ["tests.market"]
|
||||||
WSGI_APPLICATION = "tests.test_project.wsgi.application"
|
WSGI_APPLICATION = "tests.test_project.wsgi.application"
|
||||||
ROOT_URLCONF = "tests.test_project.urls"
|
ROOT_URLCONF = "tests.test_project.urls"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue