我正在写一些基于Django的免费软件。
我有一个Item
类,描述了一个定价计划(例如“订阅,每周10美元,没有试用期”)。
我的代码经常基于现有代码创建新项目。例如,基于上述项目创建的新项目为:“订阅,每周10美元,试用期为10天”(如果客户已经付款10天)。
现在有两种项目:
现在麻烦了
./manage.py loaddata ...
命令创建了预定义的项目,该命令从JSON文件加载了这些项目。./manage.py loaddata ...
,那么loaddata
命令(根据我的理解)可能会覆盖其中一个修改过的项目(稍后由我的Python创建)代码)。如何避免用新的预定义项目覆盖修改的项目?更笼统地说,如何使预定义项目和修改后的项目保持不同,以确保代码可以区分哪些项目是预定义的,哪些不是?
答案 0 :(得分:0)
转储数据和Loaddata不应用于创建“修改后的项目”。也许将这些命令更像是“备份”和“还原”。如果要从json文件加载新创建的项目,请编写自定义管理命令:
答案 1 :(得分:0)
首先声明一个抽象模型:
import os
import os.path
import sys
import datetime
import webbrowser
import argparse
import time
import traceback
import selenium
import selenium.webdriver.chrome.options
import pathlib
# ======== Command args singleton
class CommandArgs:
def __init__ (self):
self.argParser = argparse.ArgumentParser ()
self.argParser.add_argument ('-de', '--dextex', help = "show extended exception reports", action = 'store_true')
self.argParser.add_argument ('-f', '--fcall', help = 'test fast calls', action = 'store_true')
self.argParser.add_argument ('-i', '--inst', help = 'installed version rather than new one', action = 'store_true')
self.argParser.add_argument ('-b', '--blind', help = 'don\'t start browser', action = 'store_true')
self.argParser.add_argument ('-u', '--unattended', help = 'unattended mode', action = 'store_true')
self.__dict__.update (self.argParser.parse_args () .__dict__)
commandArgs = CommandArgs ()
# ======== Browser controller singleton
class BrowserController:
def __init__ (self):
self.options = selenium.webdriver.chrome.options.Options ()
self.options.add_argument ('start-maximized')
if commandArgs.unattended:
self.options.add_argument ('--headless') # Runs Chrome in headless mode.
self.options.add_argument ('--no-sandbox') # Bypass OS security model
self.options.add_argument ('--disable-gpu') # Applicable to windows OS only
self.options.add_argument ('disable-infobars')
self.options.add_argument ('--disable-extensions')
self.webDriver = selenium.webdriver.Chrome (chrome_options = self.options)
self.browserIsRunning = False
def open (self, url):
print (f'Browser controller is opening URL: {url}')
if self.browserIsRunning:
if commandArgs.unattended:
# ---- Show in existing tab
self.webDriver.execute_script (f'window.location.href = "{url}";')
else:
# ---- Open new tab
self.webDriver.execute_script (f'window.open ("{url}","_blank");')
else:
# ---- Open browser and default tab
self.webDriver.get (url)
self.browserIsRunning = True
try:
self.message = self.webDriver.find_element_by_id ('message')
print (f'Back to back autotest, result: {self.message.text.upper ()}')
if 'succeeded' in self.message.text:
return True
else:
return False
except:
print ('No back to back autotest')
return True
browserController = BrowserController ()
# ======== Preparations
relSourcePrepathsOfErrors = []
nodeServerUrl = 'http://localhost:8090'
pythonHttpServerUrl = 'http://localhost:8000'
transpileCommand = 'transcrypt' if commandArgs.inst else 'run_transcrypt'
shipDir = os.path.dirname (os.path.abspath (__file__)) .replace ('\\', '/')
appRootDir = '/'.join (shipDir.split ('/')[ : -2])
print (f'\nApplication root directory: {appRootDir}\n')
def getAbsPath (relPath):
return '{}/{}'.format (appRootDir, relPath)
os.system ('cls' if os.name == 'nt' else 'clear')
# ---- Start an http server in the Transcryp/transcrypt directory
if not commandArgs.blind:
if commandArgs.unattended:
os.system (f'python -m http.server --directory {appRootDir} &')
else:
os.system (f'start python -m http.server --directory {appRootDir}')
# ---- Allow visual check of all command line options
os.system (f'{transpileCommand} -h')
# ======== Individual test function
def test (relSourcePrepath, run, extraSwitches, messagePrename = '', nodeJs = False, build = True, pause = 0, needsAttention = False):
if commandArgs.unattended and needsAttention:
return # This test shouldn't be done, since it can't run unattended
print (f'\n\n******** BEGIN TEST {relSourcePrepath} ********\n')
time.sleep (pause)
# ---- Compute some slugs
sourcePrepath = getAbsPath (relSourcePrepath)
sourcePrepathSplit = relSourcePrepath.split ("/")
relTargetDir = f'{"/".join (sourcePrepathSplit [:-1])}/__target__'
targetDir = getAbsPath (relTargetDir)
moduleName = sourcePrepathSplit [-1]
targetPrepath = f'{targetDir}/{moduleName}'
relMessagePrepath = f'{relTargetDir}/{messagePrename}'
messagePrepath = getAbsPath (relMessagePrepath)
# ---- If there are relevant console messages of the compilation process,
# like with the static typechecking tests, write them into a file that can be served for a visual check
if not os.path.exists (targetDir):
os.makedirs (targetDir) # Transcrypt will make targetDir too late, so it has to happen here
redirect = f' > {messagePrepath}.out' if messagePrename else ''
# ---- Default switches
defaultSwitches = '-da -sf -de -m -n '
if commandArgs.dextex:
defaultSwitches += '-de '
if build:
defaultSwitches += '-b '
# ---- Compile with Transcrypt
os.system (f'{transpileCommand} {defaultSwitches}{extraSwitches}{sourcePrepath}{redirect}')
# ---- Run with CPython to generate HTML file with back to back reference info
if run:
os.system (f'{transpileCommand} -r {defaultSwitches}{extraSwitches}{sourcePrepath}')
# ---- Apply rollup to obtain monolith, since node doesn't support named imports and exports
if nodeJs:
os.system (f'rollup {targetPrepath}.js --o {targetPrepath}.bundle.js --f cjs')
if not commandArgs.blind:
if nodeJs:
os.system (f'start cmd /k node {targetPrepath}.bundle.js'.format (moduleName))
time.sleep (5)
url = nodeServerUrl
else:
url = f'{pythonHttpServerUrl}/{relSourcePrepath}.html'
success = browserController.open (url)
if commandArgs.unattended and not success:
relSourcePrepathsOfErrors.append (relSourcePrepath)
print (f'\n******** END TEST {relSourcePrepath} ********\n\n')
# ======== Perform individual tests
for switches in (('', '-f ') if commandArgs.fcall else ('',)):
test ('development/automated_tests/hello/autotest', True, switches)
test ('development/automated_tests/transcrypt/autotest', True, switches + '-c -xr -xg ')
test ('development/automated_tests/time/autotest', True, switches, needsAttention = True)
test ('development/automated_tests/re/autotest', True, switches)
test ('development/manual_tests/module_random/module_random', False, switches)
test ('development/manual_tests/transcrypt_only/transcrypt_only', False, switches)
test ('development/manual_tests/transcrypt_and_python_results_differ/results', False, switches)
test ('development/manual_tests/static_types/static_types', False, switches + '-ds -dc ', messagePrename = 'static_types')
test ('development/manual_tests/async_await/test', False, switches)
test ('demos/nodejs_demo/nodejs_demo', False, switches, nodeJs = True)
test ('demos/terminal_demo/terminal_demo', False, switches, needsAttention = True)
test ('demos/hello/hello', False, switches, needsAttention = False)
test ('demos/jquery_demo/jquery_demo', False, switches)
test ('demos/d3js_demo/d3js_demo', False, switches)
test ('demos/ios_app/ios_app', False, switches)
test ('demos/react_demo/react_demo', False, switches)
test ('demos/riot_demo/riot_demo', False, switches)
test ('demos/plotly_demo/plotly_demo', False, switches)
test ('demos/three_demo/three_demo', False, switches)
test ('demos/pong/pong', False, switches)
test ('demos/pysteroids_demo/pysteroids', False, switches)
test ('demos/turtle_demos/star', False, switches, pause = 2)
test ('demos/turtle_demos/snowflake', False, switches, pause = 2)
test ('demos/turtle_demos/mondrian', False, switches, pause = 2)
test ('demos/turtle_demos/mandala', False, switches, pause = 2)
test ('demos/cyclejs_demo/cyclejs_demo', False, switches)
test ('demos/cyclejs_demo/cyclejs_http_demo', False, switches, build = False)
test ('demos/cyclejs_demo/component_demos/isolated_bmi_slider/bmi', False, switches)
test ('demos/cyclejs_demo/component_demos/labeled_slider/labeled_slider', False, switches)
test ('tutorials/baseline/bl_010_hello_world/hello_world', False, switches)
test ('tutorials/baseline/bl_020_assign/assign', False, switches)
test ('tutorials/baseline/bl_030_if_else_prompt/if_else_prompt', False, switches, needsAttention = True)
test ('tutorials/baseline/bl_035_if_else_event/if_else_event', False, switches, needsAttention = True)
test ('tutorials/baseline/bl_040_for_simple/for_simple', False, switches)
test ('tutorials/baseline/bl_042_for_nested/for_nested', False, switches)
test ('tutorials/baseline/bl_045_while_simple/while_simple', False, switches, needsAttention = True)
test ('tutorials/static_typing/static_typing', False, switches + '-c -ds ', messagePrename = 'static_typing')
if relSourcePrepathsOfErrors:
print ('\n\n!!!!!!!!!!!!!!!!!!!!\n')
for relSourcePrepathOfError in relSourcePrepathsOfErrors:
print (f'SHIPMENT TEST ERROR: {relSourcePrepathOfError}')
print ('\n!!!!!!!!!!!!!!!!!!!!\n\n')
print ('\nSHIPMENT TEST FAILED\n')
sys.exit (1)
else:
# ---- Make docs, the resulting files are untracked
if not commandArgs.unattended:
origDir = os.getcwd ()
sphinxDir = '/'.join ([appRootDir, 'docs/sphinx'])
os.chdir (sphinxDir)
os.system ('touch *.rst')
os.system ('make html')
os.chdir (origDir)
# ---- Terminate
print ('\nSHIPMENT TEST SUCCEEDED\n')
sys.exit (0)
然后声明:
class ItemBase(models.Model):
class Meta:
abstract = True
name = models.CharField(max_length=100)
# ...
class PredefinedItem(ItemBase):
pass
class ModifiedItem(ItemBase):
base = models.OneToOneField(PredefinedItem, null=True)
@atomic
@staticmethod
def obtain_predefined(id):
try:
return ModifiedItem.objects.get(base_id=id)
except ModifiedItem.DoesNotExist:
predefined = PredefinedItem.objects.get(pk=id)
return ModifiedItem.objects.create(base=id, **model_to_dict(
predefined,
fields=[f.name for f in ItemBase._meta.fields]))
允许创建预定义对象的副本,使用该副本代替预定义对象本身。因此,我们不必担心预定义的对象会覆盖已修改的对象。