bb8826d54ad616041ea39b7f6eb50a19bb21c7a4
[zzz-pokedex.git] / pokedex / db / tables.py
1 # encoding: utf8
2
3 from sqlalchemy import Column, ForeignKey, MetaData, Table
4 from sqlalchemy.ext.declarative import declarative_base
5 from sqlalchemy.ext.associationproxy import association_proxy
6 from sqlalchemy.orm import backref, relation
7 from sqlalchemy.types import *
8 from sqlalchemy.databases.mysql import *
9
10 from pokedex.db import rst
11
12 metadata = MetaData()
13 TableBase = declarative_base(metadata=metadata)
14
15 class Ability(TableBase):
16 __tablename__ = 'abilities'
17 __singlename__ = 'ability'
18 id = Column(Integer, primary_key=True, nullable=False)
19 name = Column(Unicode(24), nullable=False)
20 flavor_text = Column(Unicode(64), nullable=False)
21 effect = Column(Unicode(255), nullable=False)
22
23 class ContestEffect(TableBase):
24 __tablename__ = 'contest_effects'
25 id = Column(Integer, primary_key=True, nullable=False)
26 appeal = Column(SmallInteger, nullable=False)
27 jam = Column(SmallInteger, nullable=False)
28 flavor_text = Column(Unicode(64), nullable=False)
29 effect = Column(Unicode(255), nullable=False)
30
31 class EggGroup(TableBase):
32 __tablename__ = 'egg_groups'
33 id = Column(Integer, primary_key=True, nullable=False)
34 name = Column(Unicode(16), nullable=False)
35
36 class Encounter(TableBase):
37 """Rows in this table represent encounters with wild Pokémon.
38
39 Within a given area in a given game, encounters are differentiated by the
40 slot they are in and a world condition.
41
42 Groups of slots belong to encounter types; these are what the player is
43 doing to get an encounter, such as surfing or walking through tall grass.
44
45 Within an encounter type, slots are defined primarily by rarity. Each slot
46 can also be affected by a world condition; for example, the 20% slot for
47 walking in tall grass is affected by whether a swarm is in effect in the
48 areas. "There is a swarm" and "there is not a swarm" are conditions, and
49 together they make a condition group. However, since "not a swarm" is a
50 base state rather than any sort of new state, it is omitted and instead
51 referred to by a NULL.
52
53 A slot (20% walking in grass) and single world condition (NULL, i.e. no
54 swarm) are thus enough to define a specific encounter.
55
56 Well, okay, almost: each slot actually appears twice.
57 """
58
59 __tablename__ = 'encounters'
60 id = Column(Integer, primary_key=True, nullable=False)
61 version_id = Column(Integer, ForeignKey('versions.id'), nullable=False, autoincrement=False)
62 location_area_id = Column(Integer, ForeignKey('location_areas.id'), nullable=False, autoincrement=False)
63 encounter_type_slot_id = Column(Integer, ForeignKey('encounter_type_slots.id'), nullable=False, autoincrement=False)
64 encounter_condition_id = Column(Integer, ForeignKey('encounter_conditions.id'), nullable=True, autoincrement=False)
65 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), nullable=False, autoincrement=False)
66 min_level = Column(Integer, nullable=False, autoincrement=False)
67 max_level = Column(Integer, nullable=False, autoincrement=False)
68
69 class EncounterCondition(TableBase):
70 """Rows in this table represent something different about the world that
71 can affect what Pokémon are encountered.
72 """
73
74 __tablename__ = 'encounter_conditions'
75 id = Column(Integer, primary_key=True, nullable=False)
76 encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=False, autoincrement=False)
77 name = Column(Unicode(64), nullable=False)
78
79 class EncounterConditionGroup(TableBase):
80 """Rows in this table represent a group of mutually exclusive conditions,
81 such as morning/day/night. "Conditions" that are part of the default state
82 of the world, such as "not during a swarm" or "not using the PokéRadar",
83 are not included in this table and are referred to by NULLs in other
84 tables.
85 """
86
87 __tablename__ = 'encounter_condition_groups'
88 id = Column(Integer, primary_key=True, nullable=False)
89 name = Column(Unicode(64), nullable=False)
90
91 class EncounterType(TableBase):
92 """Rows in this table represent ways the player can enter a wild encounter;
93 i.e. surfing, fishing, or walking through tall grass.
94 """
95
96 __tablename__ = 'encounter_types'
97 id = Column(Integer, primary_key=True, nullable=False)
98 name = Column(Unicode(64), nullable=False)
99
100 class EncounterTypeSlot(TableBase):
101 """Rows in this table represent an abstract "slot" within an encounter
102 type, associated with both a condition group and a rarity.
103
104 Note that there are two encounters per slot, so the rarities will only add
105 up to 50.
106 """
107
108 __tablename__ = 'encounter_type_slots'
109 id = Column(Integer, primary_key=True, nullable=False)
110 encounter_type_id = Column(Integer, ForeignKey('encounter_types.id'), primary_key=False, nullable=False, autoincrement=False)
111 encounter_condition_group_id = Column(Integer, ForeignKey('encounter_condition_groups.id'), primary_key=False, nullable=True, autoincrement=False)
112 rarity = Column(Integer, nullable=False, autoincrement=False)
113
114 class EvolutionChain(TableBase):
115 __tablename__ = 'evolution_chains'
116 id = Column(Integer, primary_key=True, nullable=False)
117 growth_rate_id = Column(Integer, ForeignKey('growth_rates.id'), nullable=False)
118 steps_to_hatch = Column(Integer, nullable=False)
119 baby_trigger_item = Column(Unicode(12))
120
121 class EvolutionMethod(TableBase):
122 __tablename__ = 'evolution_methods'
123 id = Column(Integer, primary_key=True, nullable=False)
124 name = Column(Unicode(64), nullable=False)
125 description = Column(Unicode(255), nullable=False)
126
127 class Generation(TableBase):
128 __tablename__ = 'generations'
129 id = Column(Integer, primary_key=True, nullable=False)
130 name = Column(Unicode(16), nullable=False)
131 main_region = Column(Unicode(16), nullable=False)
132
133 class GrowthRate(TableBase):
134 """`formula` is written in LaTeX math notation."""
135 __tablename__ = 'growth_rates'
136 id = Column(Integer, primary_key=True, nullable=False)
137 name = Column(Unicode(20), nullable=False)
138 formula = Column(Unicode(500), nullable=False)
139
140 class Item(TableBase):
141 __tablename__ = 'items'
142 __singlename__ = 'item'
143 id = Column(Integer, primary_key=True, nullable=False)
144 name = Column(Unicode(16), nullable=False)
145
146 class Language(TableBase):
147 __tablename__ = 'languages'
148 id = Column(Integer, primary_key=True, nullable=False)
149 name = Column(Unicode(16), nullable=False)
150
151 class Location(TableBase):
152 __tablename__ = 'locations'
153 __singlename__ = 'location'
154 id = Column(Integer, primary_key=True, nullable=False)
155 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
156 name = Column(Unicode(64), nullable=False)
157
158 class LocationArea(TableBase):
159 __tablename__ = 'location_areas'
160 id = Column(Integer, primary_key=True, nullable=False)
161 location_id = Column(Integer, ForeignKey('locations.id'), nullable=False)
162 internal_id = Column(Integer, nullable=False)
163 name = Column(Unicode(64), nullable=True)
164
165 class LocationAreaEncounterRate(TableBase):
166 __tablename__ = 'location_area_encounter_rates'
167 location_area_id = Column(Integer, ForeignKey('location_areas.id'), primary_key=True, nullable=False, autoincrement=False)
168 encounter_type_id = Column(Integer, ForeignKey('encounter_types.id'), primary_key=True, nullable=False, autoincrement=False)
169 rate = Column(Integer, nullable=True)
170
171 class Machine(TableBase):
172 __tablename__ = 'machines'
173 machine_number = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
174 generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
175 move_id = Column(Integer, ForeignKey('moves.id'), nullable=False)
176
177 class MoveDamageClass(TableBase):
178 __tablename__ = 'move_damage_classes'
179 id = Column(Integer, primary_key=True, nullable=False)
180 name = Column(Unicode(8), nullable=False)
181 description = Column(Unicode(64), nullable=False)
182
183 class MoveEffect(TableBase):
184 __tablename__ = 'move_effects'
185 id = Column(Integer, primary_key=True, nullable=False)
186 priority = Column(SmallInteger, nullable=False)
187 short_effect = Column(Unicode(256), nullable=False)
188 effect = Column(Unicode(5120), nullable=False)
189
190 class MoveFlag(TableBase):
191 __tablename__ = 'move_flags'
192 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
193 move_flag_type_id = Column(Integer, ForeignKey('move_flag_types.id'), primary_key=True, nullable=False, autoincrement=False)
194
195 class MoveFlagType(TableBase):
196 __tablename__ = 'move_flag_types'
197 id = Column(Integer, primary_key=True, nullable=False)
198 name = Column(Unicode(32), nullable=False)
199 description = Column(rst.RstTextColumn(128), nullable=False)
200
201 class MoveName(TableBase):
202 __tablename__ = 'move_names'
203 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False)
204 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
205 name = Column(Unicode(16), nullable=False)
206
207 class MoveTarget(TableBase):
208 __tablename__ = 'move_targets'
209 id = Column(Integer, primary_key=True, nullable=False)
210 name = Column(Unicode(32), nullable=False)
211 description = Column(Unicode(128), nullable=False)
212
213 class Move(TableBase):
214 __tablename__ = 'moves'
215 __singlename__ = 'move'
216 id = Column(Integer, primary_key=True, nullable=False)
217 name = Column(Unicode(12), nullable=False)
218 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
219 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
220 power = Column(SmallInteger)
221 pp = Column(SmallInteger, nullable=False)
222 accuracy = Column(SmallInteger)
223 target_id = Column(Integer, ForeignKey('move_targets.id'), nullable=False)
224 damage_class_id = Column(Integer, ForeignKey('move_damage_classes.id'), nullable=False)
225 effect_id = Column(Integer, ForeignKey('move_effects.id'), nullable=False)
226 effect_chance = Column(Integer)
227 contest_type = Column(Unicode(8), nullable=False)
228 contest_effect_id = Column(Integer, ForeignKey('contest_effects.id'), nullable=True)
229 super_contest_effect_id = Column(Integer, ForeignKey('super_contest_effects.id'), nullable=False)
230
231 class Pokemon(TableBase):
232 """The core to this whole mess.
233
234 Note that I use both 'forme' and 'form' in both code and the database. I
235 only use 'forme' when specifically referring to Pokémon that have multiple
236 distinct species as forms—i.e., different stats or movesets. 'Form' is a
237 more general term referring to any variation within a species, including
238 purely cosmetic forms like Unown.
239 """
240 __tablename__ = 'pokemon'
241 __singlename__ = 'pokemon'
242 id = Column(Integer, primary_key=True, nullable=False)
243 name = Column(Unicode(20), nullable=False)
244 forme_name = Column(Unicode(16))
245 forme_base_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
246 generation_id = Column(Integer, ForeignKey('generations.id'))
247 evolution_chain_id = Column(Integer, ForeignKey('evolution_chains.id'))
248 evolution_parent_pokemon_id = Column(Integer, ForeignKey('pokemon.id'))
249 evolution_method_id = Column(Integer, ForeignKey('evolution_methods.id'))
250 evolution_parameter = Column(Unicode(32))
251 height = Column(Integer, nullable=False)
252 weight = Column(Integer, nullable=False)
253 species = Column(Unicode(16), nullable=False)
254 color = Column(Unicode(6), nullable=False)
255 pokemon_shape_id = Column(Integer, ForeignKey('pokemon_shapes.id'), nullable=False)
256 habitat = Column(Unicode(16), nullable=False)
257 gender_rate = Column(Integer, nullable=False)
258 capture_rate = Column(Integer, nullable=False)
259 base_experience = Column(Integer, nullable=False)
260 base_happiness = Column(Integer, nullable=False)
261 gen1_internal_id = Column(Integer)
262 is_baby = Column(Boolean, nullable=False)
263 has_gen4_fem_sprite = Column(Boolean, nullable=False)
264 has_gen4_fem_back_sprite = Column(Boolean, nullable=False)
265
266 ### Stuff to handle alternate Pokémon forms
267
268 @property
269 def national_id(self):
270 """Returns the National Pokédex number for this Pokémon. Use this
271 instead of the id directly; alternate formes may make the id incorrect.
272 """
273
274 if self.forme_base_pokemon_id:
275 return self.forme_base_pokemon_id
276 return self.id
277
278 @property
279 def full_name(self):
280 """Returns the name of this Pokémon, including its Forme, if any."""
281
282 if self.forme_name:
283 return "%s %s" % (self.forme_name.capitalize(), self.name)
284 return self.name
285
286 @property
287 def normal_form(self):
288 """Returns the normal form for this Pokémon; i.e., this will return
289 regular Deoxys when called on any Deoxys form.
290 """
291
292 if self.forme_base_pokemon:
293 return self.forme_base_pokemon
294
295 return self
296
297 class PokemonAbility(TableBase):
298 __tablename__ = 'pokemon_abilities'
299 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
300 ability_id = Column(Integer, ForeignKey('abilities.id'), nullable=False)
301 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
302
303 class PokemonDexNumber(TableBase):
304 __tablename__ = 'pokemon_dex_numbers'
305 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
306 generation_id = Column(Integer, ForeignKey('generations.id'), primary_key=True, nullable=False, autoincrement=False)
307 pokedex_number = Column(Integer, nullable=False)
308
309 class PokemonEggGroup(TableBase):
310 __tablename__ = 'pokemon_egg_groups'
311 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
312 egg_group_id = Column(Integer, ForeignKey('egg_groups.id'), primary_key=True, nullable=False, autoincrement=False)
313
314 class PokemonFlavorText(TableBase):
315 __tablename__ = 'pokemon_flavor_text'
316 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
317 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
318 flavor_text = Column(Unicode(255), nullable=False)
319
320 class PokemonFormGroup(TableBase):
321 __tablename__ = 'pokemon_form_groups'
322 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
323 description = Column(Unicode(512), nullable=False)
324
325 class PokemonFormSprite(TableBase):
326 __tablename__ = 'pokemon_form_sprites'
327 id = Column(Integer, primary_key=True, nullable=False)
328 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
329 name = Column(Unicode(16), nullable=True)
330
331 class PokemonItem(TableBase):
332 __tablename__ = 'pokemon_items'
333 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
334 version_id = Column(Integer, ForeignKey('versions.id'), primary_key=True, nullable=False, autoincrement=False)
335 item_id = Column(Integer, ForeignKey('items.id'), primary_key=True, nullable=False, autoincrement=False)
336 rarity = Column(Integer, nullable=False)
337
338 class PokemonMove(TableBase):
339 __tablename__ = 'pokemon_moves'
340 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
341 version_group_id = Column(Integer, ForeignKey('version_groups.id'), primary_key=True, nullable=False, autoincrement=False)
342 move_id = Column(Integer, ForeignKey('moves.id'), primary_key=True, nullable=False, autoincrement=False, index=True)
343 pokemon_move_method_id = Column(Integer, ForeignKey('pokemon_move_methods.id'), primary_key=True, nullable=False, autoincrement=False)
344 level = Column(Integer, primary_key=True, nullable=True, autoincrement=False)
345 order = Column(Integer, nullable=True)
346
347 class PokemonMoveMethod(TableBase):
348 __tablename__ = 'pokemon_move_methods'
349 id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
350 name = Column(Unicode(64), nullable=False)
351 description = Column(Unicode(255), nullable=False)
352
353 class PokemonName(TableBase):
354 __tablename__ = 'pokemon_names'
355 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
356 language_id = Column(Integer, ForeignKey('languages.id'), primary_key=True, nullable=False, autoincrement=False)
357 name = Column(Unicode(16), nullable=False)
358
359 class PokemonShape(TableBase):
360 __tablename__ = 'pokemon_shapes'
361 id = Column(Integer, primary_key=True, nullable=False)
362 name = Column(Unicode(24), nullable=False)
363 awesome_name = Column(Unicode(16), nullable=False)
364
365 class PokemonStat(TableBase):
366 __tablename__ = 'pokemon_stats'
367 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
368 stat_id = Column(Integer, ForeignKey('stats.id'), primary_key=True, nullable=False, autoincrement=False)
369 base_stat = Column(Integer, nullable=False)
370 effort = Column(Integer, nullable=False)
371
372 class PokemonType(TableBase):
373 __tablename__ = 'pokemon_types'
374 pokemon_id = Column(Integer, ForeignKey('pokemon.id'), primary_key=True, nullable=False, autoincrement=False)
375 type_id = Column(Integer, ForeignKey('types.id'), nullable=False)
376 slot = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
377
378 class Stat(TableBase):
379 __tablename__ = 'stats'
380 id = Column(Integer, primary_key=True, nullable=False)
381 name = Column(Unicode(16), nullable=False)
382
383 class SuperContestEffect(TableBase):
384 __tablename__ = 'super_contest_effects'
385 id = Column(Integer, primary_key=True, nullable=False)
386 appeal = Column(SmallInteger, nullable=False)
387 flavor_text = Column(Unicode(64), nullable=False)
388
389 class TypeEfficacy(TableBase):
390 __tablename__ = 'type_efficacy'
391 damage_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
392 target_type_id = Column(Integer, ForeignKey('types.id'), primary_key=True, nullable=False, autoincrement=False)
393 damage_factor = Column(Integer, nullable=False)
394
395 class Type(TableBase):
396 __tablename__ = 'types'
397 __singlename__ = 'type'
398 id = Column(Integer, primary_key=True, nullable=False)
399 name = Column(Unicode(8), nullable=False)
400 abbreviation = Column(Unicode(3), nullable=False)
401
402 class VersionGroup(TableBase):
403 __tablename__ = 'version_groups'
404 id = Column(Integer, primary_key=True, nullable=False)
405 generation_id = Column(Integer, ForeignKey('generations.id'), nullable=False)
406
407 class Version(TableBase):
408 __tablename__ = 'versions'
409 id = Column(Integer, primary_key=True, nullable=False)
410 version_group_id = Column(Integer, ForeignKey('version_groups.id'), nullable=False)
411 name = Column(Unicode(32), nullable=False)
412
413
414 ### Relations down here, to avoid ordering problems
415 Encounter.pokemon = relation(Pokemon, backref='encounters')
416 Encounter.version = relation(Version, backref='encounters')
417 Encounter.location_area = relation(LocationArea, backref='encounters')
418 Encounter.slot = relation(EncounterTypeSlot, backref='encounters')
419 Encounter.condition = relation(EncounterCondition, backref='encounters')
420
421 EncounterCondition.group = relation(EncounterConditionGroup,
422 backref='conditions')
423
424 EncounterTypeSlot.type = relation(EncounterType, backref='slots')
425
426 EvolutionChain.growth_rate = relation(GrowthRate, backref='evolution_chains')
427
428 Generation.versions = relation(Version, secondary=VersionGroup.__table__)
429
430 LocationArea.location = relation(Location, backref='areas')
431
432 Machine.generation = relation(Generation)
433
434 Move.contest_effect = relation(ContestEffect, backref='moves')
435 Move.damage_class = relation(MoveDamageClass, backref='moves')
436 Move.flags = association_proxy('move_flags', 'flag')
437 Move.foreign_names = relation(MoveName, backref='pokemon')
438 Move.generation = relation(Generation, backref='moves')
439 Move.machines = relation(Machine, backref='move')
440 Move.move_effect = relation(MoveEffect, backref='moves')
441 Move.move_flags = relation(MoveFlag, backref='move')
442 Move.super_contest_effect = relation(SuperContestEffect, backref='moves')
443 Move.target = relation(MoveTarget, backref='moves')
444 Move.type = relation(Type, backref='moves')
445
446 Move.effect = rst.MoveEffectProperty('effect')
447 Move.priority = association_proxy('move_effect', 'priority')
448 Move.short_effect = rst.MoveEffectProperty('short_effect')
449
450 MoveFlag.flag = relation(MoveFlagType)
451
452 MoveName.language = relation(Language)
453
454 Pokemon.abilities = relation(Ability, secondary=PokemonAbility.__table__,
455 order_by=PokemonAbility.slot,
456 backref='pokemon')
457 Pokemon.formes = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.forme_base_pokemon_id,
458 backref=backref('forme_base_pokemon',
459 remote_side=[Pokemon.id]))
460 Pokemon.dex_numbers = relation(PokemonDexNumber, backref='pokemon')
461 Pokemon.egg_groups = relation(EggGroup, secondary=PokemonEggGroup.__table__,
462 order_by=PokemonEggGroup.egg_group_id,
463 backref='pokemon')
464 Pokemon.evolution_chain = relation(EvolutionChain, backref='pokemon')
465 Pokemon.evolution_method = relation(EvolutionMethod)
466 Pokemon.evolution_children = relation(Pokemon, primaryjoin=Pokemon.id==Pokemon.evolution_parent_pokemon_id,
467 backref=backref('evolution_parent',
468 remote_side=[Pokemon.id]))
469 Pokemon.flavor_text = relation(PokemonFlavorText, backref='pokemon')
470 Pokemon.foreign_names = relation(PokemonName, backref='pokemon')
471 Pokemon.items = relation(PokemonItem)
472 Pokemon.generation = relation(Generation, backref='pokemon')
473 Pokemon.shape = relation(PokemonShape, backref='pokemon')
474 Pokemon.stats = relation(PokemonStat, backref='pokemon')
475 Pokemon.types = relation(Type, secondary=PokemonType.__table__)
476
477 PokemonDexNumber.generation = relation(Generation)
478
479 PokemonFlavorText.version = relation(Version)
480
481 PokemonItem.item = relation(Item, backref='pokemon')
482 PokemonItem.version = relation(Version)
483
484 PokemonFormGroup.pokemon = relation(Pokemon, backref=backref('form_group',
485 uselist=False))
486 PokemonFormSprite.pokemon = relation(Pokemon, backref='form_sprites')
487
488 PokemonMove.pokemon = relation(Pokemon, backref='pokemon_moves')
489 PokemonMove.version_group = relation(VersionGroup)
490 PokemonMove.move = relation(Move, backref='pokemon_moves')
491 PokemonMove.method = relation(PokemonMoveMethod)
492
493 PokemonName.language = relation(Language)
494
495 PokemonStat.stat = relation(Stat)
496
497 Type.damage_efficacies = relation(TypeEfficacy,
498 primaryjoin=Type.id
499 ==TypeEfficacy.damage_type_id,
500 backref='damage_type')
501 Type.target_efficacies = relation(TypeEfficacy,
502 primaryjoin=Type.id
503 ==TypeEfficacy.target_type_id,
504 backref='target_type')
505
506 Version.version_group = relation(VersionGroup, backref='versions')
507 Version.generation = association_proxy('version_group', 'generation')
508
509 VersionGroup.generation = relation(Generation, backref='version_groups')