Concepts
An Anki package (.apkg) is a zip file that holds a small Anki collection and the media files that notes refer to. This page describes the objects genanki2 uses to model it and the identifiers that make a new version of a deck import as an update and not make duplicates.
Package and collection
A package holds a small Anki collection and genanki2 models it with the same objects that Anki uses. package() returns two objects:
- The package holds the media files and handles reading and writing to disk, through
Package.read_apkg()andpkg.write_apkg(). - The collection holds the decks, notetypes, notes and cards, among other things. It can also be read and written to disk, independently of the package, using
Collection.read_sqlite()andcollection.write_sqlite().
from genanki2 import package
pkg, collection = package()
from genanki2 import Collection, Package
collection = Collection() # An empty collection
# Although Anki does not start from a completely empty collection.
# default() adds the default config entries, which are required
collection = Collection.default()
# anki_default() also adds the default deck, preset and builtin notetypes, but are technically optional
collection = Collection.anki_default()
pkg = Package(collection=collection)
Package
A package has two attributes:
pkg.collection: the collection it writes.pkg.media: the paths of the media files it writes. A field refers to a file by its file name only, so two paths ending in the same name collide:
A package (called "source") read withpkg.media.append("audio/it_casa.mp3") pkg.media.append("more_audio/it_casa.mp3") # Raises on the media checkkeep_source_media=Truekeeps track of media files in it and adds them to the written package, but in a separate list. See Reading a package.
Use Package.read_apkg() and pkg.write_apkg() for disk I/O.
A CollectionPackage is basically the same thing (same attributes, mostly the same methods), but it is interpreted as a whole collection backup and uses a different file extension, .colpkg. See Collection packages.
Collection
The collection holds a list per table, either what was read or added manually:
collection.deck_configsfor deck presetscollection.decksfor deckscollection.notetypesfor notetypes. A notetype contains all of its fields and templates inside it, so there are no dedicated lists for them.collection.notesfor notescollection.tagsfor tags (this is ignored during import; it is present only for completeness' sake)collection.cardsfor cardscollection.reviewsfor revision historycollection.gravesfor deleted objects (also ignored)collection.configfor configuration entriescollection.metadatafor metadata, like creation time (this is always a 1 row table)
The lists do not have to be filled by hand. add_notes() adds the cards to collection.cards, and the write process collects everything the cards refer to: the notes, their notetypes, the decks and the decks' presets. See Building a deck.
Some relevant methods:
add_notes()returns the new cards, so they can be changed before a write (see Customizing cards).generate_missing_cards()makes the cards that an edit adds, andremove_notes()removes notes together with their cards (see Cards after an edit and Removing notes).- To find objects, use the lookup methods such as
collection.get_deck(name=...)andcollection.get_note(guid=...)(see Reading a package). Collection.read_sqlite()andcollection.write_sqlite()for disk I/O. You can read only the collection inside a package usingCollection.from_apkg().
A collection's objects
Notetypes, fields and templates
A notetype says which fields a note has and which cards it produces. Each card type is a template, with a front and a back written in Anki's template syntax.
A notetype is either standard or cloze. A cloze notetype has a single template and makes one card per cloze number in the note ({{c1::...}}, {{c2::...}}). See Cloze notetypes.
from genanki2 import notetype
# A template is a (name, front, back) tuple
word = notetype(
"Italian word",
["Italian", "English"],
[("Recognition", "{{Italian}}", "{{FrontSide}}<hr id=answer>{{English}}")],
id=1_759_600_000_000,
)
from genanki2 import Field, Notetype, Template, TemplateConfig
word = Notetype(
id=1_759_600_000_000,
name="Italian word",
fields=[Field(name="Italian"), Field(name="English")],
templates=[
Template(
name="Recognition",
config=TemplateConfig(
q_format="{{Italian}}", # the front
a_format="{{FrontSide}}<hr id=answer>{{English}}", # the back
),
),
],
)
Notes
A note is one entry: a value for each field of its notetype.
note.fields holds the values in the order of the notetype's fields, and note["English"] reads or sets one by name. The values are HTML, and a note refers to a media file by its name, as in [sound:it_casa.mp3] (see Building a deck).
from genanki2 import guid_for, note
casa = note(word, ["casa", "house"], tags=["noun"], guid=guid_for(word.id, "casa"))
from genanki2 import Note, guid_for
casa = Note(
notetype=word,
fields=["casa", "house"],
tags=["noun"],
guid=guid_for(word.id, "casa"),
)
Decks and presets
A deck holds cards. Its study options come from a preset (in Anki's interface, the deck's Options). :: in a name makes a subdeck: Italian::Vocabulary is Vocabulary inside Italian.
A deck is either normal or filtered:
- A normal deck owns its cards and has a preset.
- A filtered deck has no preset: it gathers cards from normal decks by a search, so they can be studied apart for a while, and they return to their home deck when it is emptied. A card in a filtered deck has the filtered deck as
card.deckand its home deck ascard.original_deck. Usedeck.is_normal()/deck.is_filtered()to tell them apart. Filtered decks are usually built by users in Anki, so they mostly show up when reading someone's collection backup. See Filtered decks on building one.
from genanki2 import deck, deck_config
slow = deck_config("Italian, slow", id=1_759_600_000_100, new_per_day=5)
vocabulary = deck("Italian::Vocabulary", config=slow)
from genanki2 import Deck, DeckConfig, DeckConfigInner
slow = DeckConfig(
id=1_759_600_000_100, name="Italian, slow", config=DeckConfigInner(new_per_day=5)
)
vocabulary = Deck(name="Italian::Vocabulary", config=slow)
Cards
A card is one thing to study. A note gets one card per template whose front is not empty, or one card per cloze number on a cloze notetype.
A card holds the study progress (card_type, queue, due, interval_days and others), so this is where a user's schedule lives. Every card in a newly built package is new.
# One tuple of cards per note, with a card for each template that renders
cards = collection.add_notes([casa], vocabulary)
recognition = cards[0][0]
from genanki2 import Card
# The template's position, and the new-card position add_notes() would give
recognition = Card(note=casa, deck=vocabulary, template_ord=0, due=1)
collection.cards.append(recognition)
Identifiers that keep a deck updatable
When someone imports a new version of a deck that they already have, Anki decides what is an update by comparing identifiers:
- notetypes by
idand then their fields and templates, by id (if present) or by name - notes by
guid - decks by name
- presets by
id
An object whose identifier changed between versions arrives as a second copy, next to the first. You can spot them because Anki appends a "+" to the name (something similar to "My Notetype" and "My Notetype+").
A deck that will be rebuilt needs identifiers that stay the same on every build:
- Notetype ids. Without
id=,notetype()makes a new one each run using themonotonic_id()function. Pass a fixed number, any integer chosen once. A millisecond timestamp is what Anki uses. - Field and template ids. A notetype whose fields or templates change (added, removed, reordered, or renamed without an id) arrives as a copy even with the same notetype id.
Notetype.fill_missing_ids()gives them ids, but random ones, so a rebuild script should call it only in the first build or should pass fixed ones (field("Italian", id=...)). See Changing a notetype's fields or templates. - Note guids. Without
guid=, a note gets a random one each run using theguid()function.guid_for(notetype.id, key)derives a guid from a stable key that names the note in your data: a word, a row id, a file name. Pass several key parts when no single one is unique (guid_for(notetype.id, key1, key2, ...)). Do not derive it from the whole note: a corrected translation would then change the guid, and the corrected note would arrive as a duplicate beside the old one. - Deck names have to stay the same. A renamed deck arrives as a new deck, and the cards that users already have stay in the old one.
Alternatively, build a deck only once, the first time, and never rebuild it again. Any updates you need to make can be done by a read-update-write flow using genanki2 (see Updating a package). Thus, you don't have to hardcode and keep track of unique ids or guids.
Cards have no identifier of their own: the import never updates a card that a user already has, as Changing cards explains.