Friday, November 3, 2023¶
I had a not-so-trivial failure:
(dev) luc@yoga:~/work/book$ dt docs/specs/migrate.rst
**********************************************************************
File "docs/specs/migrate.rst", line 87, in migrate.rst
Failed example:
shell("python manage.py prep --noinput")
#doctest: +ELLIPSIS +REPORT_UDIFF +NORMALIZE_WHITESPACE
Differences (unified diff with -expected +actual):
@@ -1,3 +1,256 @@
-`initdb std minimal_ledger demo demo2 demo_bookings checksummaries checkdata` started on database .../default.db.
-...
-Installed 4030 object(s) from 31 fixture(s)
+`initdb std minimal_ledger demo demo2 demo_bookings checksummaries checkdata` started on database /home/luc/work/book/lino_book/projects/migs/default.db.
+Migrations for 'calview':
+ migrations/calview/0001_initial.py
+ - Create model DailyPlannerRow
+Migrations for 'countries':
+ migrations/countries/0001_initial.py
+ - Create model Country
+ - Create model Place
+Migrations for 'linod':
+ migrations/linod/0001_initial.py
+ - Create model BackgroundTask
+Migrations for 'pages':
+ migrations/pages/0001_initial.py
+ - Create model Node
+ - Create model Page
+Migrations for 'userstats':
+ migrations/userstats/0001_initial.py
+ - Create model UserStat
+Migrations for 'cal':
+ migrations/cal/0001_initial.py
+ - Create model Calendar
+ - Create model EntryRepeater
+ - Create model Event
+ - Create model EventPolicy
+ - Create model EventType
+ - Create model Guest
+ - Create model GuestRole
+ - Create model RecurrentEvent
+ - Create model RemoteCalendar
+ - Create model Room
+ - Create model Subscription
+ - Create model Task
+ migrations/cal/0002_initial.py
+ - Add field user to task
+ - Add field calendar to subscription
+ - Add field user to subscription
+ - Add field calendar to room
+ - Add field company to room
+ - Add field contact_person to room
+ - Add field contact_role to room
+ - Add field event_type to recurrentevent
+ - Add field user to recurrentevent
+ - Add field event to guest
+ - Add field partner to guest
+ - Add field role to guest
+ - Add field event_type to eventpolicy
+ - Add field assigned_to to event
+ - Add field company to event
+ - Add field contact_person to event
+ - Add field contact_role to event
+ - Add field event_type to event
+ - Add field owner_type to event
+ - Add field previous_page to event
+ - Add field room to event
+ - Add field user to event
+ - Add field cal_entry to entryrepeater
+ - Alter unique_together for guest (1 constraint(s))
+Migrations for 'changes':
+ migrations/changes/0001_initial.py
+ - Create model Change
+ migrations/changes/0002_initial.py
+ - Add field user to change
+Migrations for 'checkdata':
+ migrations/checkdata/0001_initial.py
+ - Create model Message
+ migrations/checkdata/0002_initial.py
+ - Add field user to message
+Migrations for 'comments':
+ migrations/comments/0001_initial.py
+ - Create model Comment
+ - Create model CommentType
+ - Create model Reaction
+ migrations/comments/0002_initial.py
+ - Add field user to reaction
+ - Add field comment_type to comment
+ - Add field owner_type to comment
+ - Add field reply_to to comment
+ - Add field user to comment
+Migrations for 'contacts':
+ migrations/contacts/0001_initial.py
+ - Create model CompanyType
+ - Create model Partner
+ - Create model RoleType
+ - Create model Company
+ - Create model Person
+ - Create model Role
+ migrations/contacts/0002_initial.py
+ - Add field payment_term to partner
+ - Add field purchase_account to partner
+ - Add field region to partner
+ - Add field company to role
+ - Add field person to role
+ - Add field type to company
+Migrations for 'lists':
+ migrations/lists/0001_initial.py
+ - Create model List
+ - Create model ListType
+ - Create model Member
+ - Add field list_type to list
+Migrations for 'system':
+ migrations/system/0001_initial.py
+ - Create model SiteConfig
+Migrations for 'users':
+ migrations/users/0001_initial.py
+ - Create model User
+ - Create model Authority
+Migrations for 'dashboard':
+ migrations/dashboard/0001_initial.py
+ - Create model Widget
+Migrations for 'groups':
+ migrations/groups/0001_initial.py
+ - Create model Group
+ - Create model Membership
+Migrations for 'tinymce':
+ migrations/tinymce/0001_initial.py
+ - Create model TextFieldTemplate
+Migrations for 'excerpts':
+ migrations/excerpts/0001_initial.py
+ - Create model ExcerptType
+ - Create model Excerpt
+Migrations for 'invoicing':
+ migrations/invoicing/0001_initial.py
+ - Create model FollowUpRule
+ - Create model Item
+ - Create model Plan
+ - Create model SalesRule
+ - Create model Tariff
+ migrations/invoicing/0002_initial.py
+ - Add field product to tariff
+ - Add field invoice_recipient to salesrule
+ migrations/invoicing/0003_initial.py
+ - Add field paper_type to salesrule
+ - Add field order to plan
+ - Add field partner to plan
+ - Add field user to plan
+ - Add field generator_type to item
+ - Add field invoice to item
+ - Add field partner to item
+ - Add field plan to item
+ - Add field source_journal to followuprule
+Migrations for 'ledger':
+ migrations/ledger/0001_initial.py
+ - Create model Account
+ - Create model AccountingPeriod
+ - Create model FiscalYear
+ - Create model Journal
+ - Create model LedgerInfo
+ - Create model PaymentTerm
+ - Create model Voucher
+ - Create model Movement
+ - Create model MatchRule
+ migrations/ledger/0002_initial.py
+ - Add field uploads_volume to journal
+ - Add field year to accountingperiod
+ - Alter unique_together for matchrule (1 constraint(s))
+Migrations for 'vat':
+ migrations/vat/0001_initial.py
+ - Create model VatAccountInvoice
+ - Create model InvoiceItem
+Migrations for 'memo':
+ migrations/memo/0001_initial.py
+ - Create model Mention
+Migrations for 'notify':
+ migrations/notify/0001_initial.py
+ - Create model Message
+Migrations for 'products':
+ migrations/products/0001_initial.py
+ - Create model Category
+ - Create model Product
+ - Create model PriceRule
+ migrations/products/0002_initial.py
+ - Add field selector to pricerule
+ - Add field parent to category
+Migrations for 'sales':
+ migrations/trading/0001_initial.py
+ - Create model PaperType
+ - Create model VatProductInvoice
+ - Create model InvoiceItem
+Migrations for 'subscriptions':
+ migrations/subscriptions/0001_initial.py
+ - Create model Subscription
+ - Create model SubscriptionPeriod
+ - Create model SubscriptionItem
+Migrations for 'tickets':
+ migrations/tickets/0001_initial.py
+ - Create model Site
+ - Create model TicketType
+ - Create model Ticket
+ - Create model CheckListItem
+Migrations for 'working':
+ migrations/working/0001_initial.py
+ - Create model SessionType
+ - Create model UserSummary
+ - Create model SiteSummary
+ - Create model Session
+ - Create model ServiceReport
+Migrations for 'storage':
+ migrations/storage/0001_initial.py
+ - Create model TransferRule
+ - Create model Provision
+ - Create model Movement
+ - Create model Filler
+ - Create model DeliveryNote
+ - Create model DeliveryItem
+ - Create model Component
+Migrations for 'uploads':
+ migrations/uploads/0001_initial.py
+ - Create model UploadType
+ - Create model Volume
+ - Create model Upload
+Traceback (most recent call last):
+ File "/home/luc/work/book/lino_book/projects/migs/manage.py", line 9, in <module>
+ execute_from_command_line(sys.argv)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
+ utility.execute()
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/__init__.py", line 436, in execute
+ self.fetch_command(subcommand).run_from_argv(self.argv)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/base.py", line 412, in run_from_argv
+ self.execute(*args, **cmd_options)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/base.py", line 458, in execute
+ output = self.handle(*args, **options)
+ File "/home/luc/work/lino/lino/management/commands/prep.py", line 43, in handle
+ super().handle(**options)
+ File "/home/luc/work/lino/lino/management/commands/initdb.py", line 261, in handle
+ call_command('migrate', '--run-syncdb', **options)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/__init__.py", line 194, in call_command
+ return command.execute(*args, **defaults)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/base.py", line 458, in execute
+ output = self.handle(*args, **options)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/base.py", line 106, in wrapper
+ res = handle_func(*args, **kwargs)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 117, in handle
+ executor = MigrationExecutor(connection, self.migration_progress_callback)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/db/migrations/executor.py", line 18, in __init__
+ self.loader = MigrationLoader(self.connection)
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/db/migrations/loader.py", line 58, in __init__
+ self.build_graph()
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/db/migrations/loader.py", line 229, in build_graph
+ self.load_disk()
+ File "/home/luc/virtualenvs/dev/lib/python3.10/site-packages/django/db/migrations/loader.py", line 120, in load_disk
+ migration_module = import_module(migration_path)
+ File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
+ return _bootstrap._gcd_import(name[level:], package, level)
+ File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
+ File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
+ File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
+ File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
+ File "<frozen importlib._bootstrap_external>", line 883, in exec_module
+ File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
+ File "/home/luc/work/book/lino_book/projects/migs/migrations/linod/0001_initial.py", line 17, in <module>
+ class Migration(migrations.Migration):
+ File "/home/luc/work/book/lino_book/projects/migs/migrations/linod/0001_initial.py", line 195, in Migration
+ lino.modlib.linod.choicelists.Procedure(
+ File "/home/luc/work/lino/lino/modlib/linod/choicelists.py", line 20, in __init__
+ name = func.__name__
+AttributeError: 'NoneType' object has no attribute '__name__'. Did you mean: '__ne__'?
This error was because the migration file linod/0001_initial.py
had
something like this:
(
"procedure",
lino.core.choicelists.ChoiceListField(
choicelist=lino.modlib.linod.choicelists.Procedures,
choices=[
(
lino.modlib.linod.choicelists.Procedure(
lino_xl.lib.cal.models.event_notification_scheduler,
every=300,
every_unit="secondly",
),
"event_notification_scheduler",
),
...
],
help_text="Pointer to an instance of Procedure.",
max_length=100,
unique=True,
verbose_name="Background procedure",
),
),
I fixed it by reviewing the deconstruct()
method of the
lino.core.choicelists.ChoiceListField
class. It now
removes the ‘choices’ key from kwargs
:
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
kwargs['choicelist'] = self.choicelist
+ kwargs.pop('choices', None)
return name, path, args, kwargs
Because the available choices are not relevant for the database structure, and Django might have problems for representing them in a migration.
This didn’t disturb until now, but I changed the signature of
Procedure.__init__()
: it now takes just the function object. And
function objects are not always serializable.