30-Development » History » Sprint/Milestone 1
amacdona@redhat.com, 01/25/2018 08:47 PM
1 | 1 | amacdona@redhat.com | # 30-Development |
---|---|---|---|
2 | |||
3 | .. warning:: |
||
4 | All documents within the 3.0-Development section should be considered temporary, and will be |
||
5 | removed or relocated prior to the release of Pulp 3.0. |
||
6 | |||
7 | 3.0 Development |
||
8 | \=============== |
||
9 | |||
10 | The goal of this section is to create a place for living documents related to the development of |
||
11 | Pulp 3.0. Some parts may grow and replace current documentation (plugin API) and others may be |
||
12 | temporary guides to making changes (translating from Mongo to Postgres). |
||
13 | |||
14 | .. toctree:: |
||
15 | :maxdepth: 3 |
||
16 | |||
17 | ~~~ |
||
18 | app-layout |
||
19 | data-modeling |
||
20 | db-translation-guide |
||
21 | rest-api |
||
22 | ~~~ |
||
23 | |||
24 | Pulp 3 and Python 3 |
||
25 | \------------------- |
||
26 | |||
27 | Pulp 3 will only support Python 3.5+. When introducing new dependencies to Pulp, |
||
28 | ensure they support Python 3 (see http://fedora.portingdb.xyz/). If they do not, |
||
29 | please file an issue in Redmine (related to https://pulp.plan.io/issues/2247) to |
||
30 | track the conversion of the dependency to support Python 3 and begin working with |
||
31 | upstream to convert the package. |
||
32 | |||
33 | Docstrings |
||
34 | \---------- |
||
35 | |||
36 | \`PUP-2 \<https://github.com/pulp/pups/blob/master/pup-0002.md>\`_ adopted Google style for |
||
37 | :ref:\`google-docstrings\`. When porting code from Pulp 2 to Pulp 3, convert all the docstrings to the |
||
38 | new style. vim-style regexes can be used to speed up the process. Together, these will convert all |
||
39 | of the parameters to Google Style:: |
||
40 | |||
41 | 1. Typed params |
||
42 | %s/\\(\\s\*\\):param\\s\\+\\(.\*\\):\\s\\+\\(.\*\\)\\n\\s\*:type\\s\\+.\\+:\\s\\+\\(.\*\\)/\\1 \\2 (\\4): \\3 |
||
43 | |||
44 | <!-- end list --> |
||
45 | |||
46 | 1. Untyped params |
||
47 | %s/\\(\\s\*\\):param\\s\\+\\(.\*\\):\\s\\+\\(.\*\\)/\\1 \\2: \\3 |
||
48 | |||
49 | Data Modeling |
||
50 | \============= |
||
51 | |||
52 | Introduction |
||
53 | \^<sup>\^\^\^\^\^\^\^\^</sup>\^ |
||
54 | |||
55 | The Pulp 3 data modeling effort is not just a translation or porting effort but instead |
||
56 | an effort to build Pulp 3 using Pulp 2. Remodeling will include changes to leverage the |
||
57 | relational capabilities of postgres but also to improve upon the Pulp 2 model. |
||
58 | |||
59 | When creating each Pulp 3 model object, consider the following: |
||
60 | |||
61 | \- Understand what the Pulp 2 model (collection) stores and how the information |
||
62 | is used by pulp. |
||
63 | |||
64 | \- Name the model/table appropriately. A table is implicitly plural. Naming a |
||
65 | table \`repository\` is more appropriate than \`repositories\`. |
||
66 | |||
67 | \- Name the model class appropriately. Each model object is a row in the table |
||
68 | an not a collection. Class names should be singular. For example, \`Repository\` |
||
69 | is a more appropriate class name than \`Repositories\`. |
||
70 | |||
71 | \- Do not use multiple inheritance (mixins) unless there is no other choice. Although |
||
72 | they are convenient, they greatly diminish the clarity of the model and as with |
||
73 | multiple inheritance in general, can result in untended behavior. With a little extra |
||
74 | effort, a proper class hierarchy is almost always possible and will be better in the end. |
||
75 | |||
76 | \- Question the existence, type, and naming of all fields. Field names beginning with \`\_\` |
||
77 | should not exist in Pulp 3. The primary key field name is \`id\` and already defined in the |
||
78 | \`Model\` base class. All \`display_name\` fields need to be renamed to \`name\`. Also, avoid |
||
79 | redundant scope by prefixing field names (or any attribute) with the name of the class. |
||
80 | For example: \`Task.task_type\`. |
||
81 | |||
82 | \- Question all methods (including @property). We **only** want those methods that encapsulate |
||
83 | significant complexity and are widely used. Most of the methods on Pulp 2 models will likely |
||
84 | not be necessary or wanted in the Pulp 3 models. Let's keep the model interface as clean |
||
85 | as possible. |
||
86 | |||
87 | \- Most string fields will be \`TextField\`. When the field is required and indexed, |
||
88 | use: \`TextField(db_index=True)\`. When required but **not** indexed, |
||
89 | use: \`TextField(blank=False, default=None)\`. This ensures integrity at the model and |
||
90 | database layer(s) and supports validation at the REST layer. |
||
91 | |||
92 | \- All fields containing **ISO-8601** strings must be converted to \`DateTimeField\`. |
||
93 | |||
94 | \- Think about **natural** keys. For example, each repository has a unique name (instead of repo_id |
||
95 | like in Pulp 2) that is known to users and is not the primary key (\`id\` is). The \`name\` is |
||
96 | the **natural** key. Don't forget the index. See: https://en.wikipedia.org/wiki/Natural_key |
||
97 | |||
98 | \- Define a \`natural_key()\` method in each model. This both documents the **natural** key and |
||
99 | will be used by the django serializer. |
||
100 | See: https://docs.djangoproject.com/en/1.8/topics/serialization/#serialization-of-natural-keys |
||
101 | |||
102 | Development Process |
||
103 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
104 | |||
105 | \- The **refactor** tasks (in Redmine) group related collections. |
||
106 | |||
107 | \- The Pulp 3 models are grouped into modules within the \`models\` package. |
||
108 | |||
109 | \- Foreach model class defined, A **refactor** task needs to be created in Redmine for |
||
110 | migrating the data. See existing examples. |
||
111 | |||
112 | \- Foreach model class defined, add a section to the **Notes** section below. |
||
113 | |||
114 | \- Add unit tests for models. Only methods encapsulating complexity need tests. |
||
115 | In addition to this, add a set of **howto** tests to demonstrate common use cases for the |
||
116 | model. They are intended to provide an example to developers and sanity testing only. |
||
117 | 100% coverage is not expected. Also, we don't need to test django itself. |
||
118 | |||
119 | Notes About Models |
||
120 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
121 | |||
122 | .. note:: This article is a stub. You can help by expanding it. |
||
123 | |||
124 | This section captures notes about each model. Developers should explain differences |
||
125 | between the Pulp 2 and Pulp 3. This is a good place to give examples of how models are intended |
||
126 | to be used or extended by plugins. Except for how to extend a class, refer to the **howto** unit |
||
127 | tests instead of including code blocks here. |
||
128 | |||
129 | Consumer |
||
130 | \^<sup>\^\^\^\^</sup>\^ |
||
131 | |||
132 | The consumer models are much like that in Pulp 2 except the fields related to the removed |
||
133 | **nodes** and **agent** functionality are gone. The \`bind\` has be replaced with a simple relation |
||
134 | that is managed by django because no addition fields on the join table are needed. |
||
135 | |||
136 | The **applicability** models/tables have not been included because **applicability** is not a generic |
||
137 | platform concept. Errata and RPM applicability is owned by the RPM plugin. That said, |
||
138 | the \`ConsumerContent\` model provides a base class for plugins to model content that is installed |
||
139 | on a consumer. |
||
140 | |||
141 | Repository |
||
142 | \^<sup>\^\^\^\^\^\^</sup>\^ |
||
143 | |||
144 | First, \`Distributor\` has been renamed to \`Publisher\` because it seems more appropriate. |
||
145 | |||
146 | The repository models include importers and publishers. The main difference being the |
||
147 | consolidation of the importer and publisher and its configuration. In the model, a |
||
148 | \`ContentAdaptor\` is the base for plugin contributed models that can be associated to a repository. |
||
149 | On importer and publisher base models, the **standard** configuration settings that were |
||
150 | separate documents in Pulp 2 are attributes of the importer and publishers itself. These models |
||
151 | follow the **master-detail** pattern. Adaptors needing additional configuration, need to extend the |
||
152 | base model (master) and add the extra fields on a new (detail) model. |
||
153 | |||
154 | The concept of a repository group distributor has been discarded. This concept and associated flows |
||
155 | were flawed in Pulp 2. |
||
156 | |||
157 | Examples: |
||
158 | |||
159 | .. code-block:: python |
||
160 | |||
161 | ~~~ |
||
162 | Class MyImporter(Importer): |
||
163 | ~~~ |
||
164 | |||
165 | ~~~ |
||
166 | field_1 = models.TextField() |
||
167 | field_2 = models.TextField() |
||
168 | ~~~ |
||
169 | |||
170 | Database Field Translation Guide |
||
171 | \================================ |
||
172 | |||
173 | Introduction |
||
174 | \------------ |
||
175 | |||
176 | Converting from mongo to postgres should be recognized from the outset as a monumental task. |
||
177 | In choosing Django as our ORM, this task has hopefully been made a little bit easier by bringing in |
||
178 | such a mature and well-supported framework. Great care has been taken to make full use of Django's |
||
179 | offerings when coming up with techniques and guidelines for converting our non-relational mongo |
||
180 | database over to postgres. |
||
181 | |||
182 | Django |
||
183 | \------ |
||
184 | |||
185 | Django has many layers, including the data model layer, the view layer, etc. This document focuses |
||
186 | on the model layer, and uses Django's terminology where applicable. Furthermore, every effort should |
||
187 | be made to adhere to existing Django functionality so that the full benefits of adopting this |
||
188 | framework can be realized. |
||
189 | |||
190 | https://docs.djangoproject.com/en/1.8/topics/db/models/ |
||
191 | |||
192 | Tutorial |
||
193 | \^<sup>\^\^\^\^</sup>\^ |
||
194 | |||
195 | If you aren't already familiar with some of the features that Django provides, part 1 of the Django |
||
196 | tutorial provides an excellent introduction. It covers things like starting a project for the first |
||
197 | time, starting the development web server, and accessing models. The tutorial pages that come after |
||
198 | part 1 are largely irrelevant for Pulp 3, but are a good exercise nonetheless for someone looking to |
||
199 | get to know Django a little bit better. |
||
200 | |||
201 | https://docs.djangoproject.com/en/1.8/intro/tutorial01/ |
||
202 | |||
203 | Django Concepts |
||
204 | \--------------- |
||
205 | |||
206 | There are specific Django concepts that deserve special attention before getting into some of the |
||
207 | finer details of how the relation data model should be structured, which are outlined in subsections |
||
208 | below. |
||
209 | |||
210 | Model Inheritance |
||
211 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
212 | |||
213 | https://docs.djangoproject.com/en/1.8/topics/db/models/#model-inheritance |
||
214 | |||
215 | Django provides three different mechanisms for supporting object-oriented inheritance in its |
||
216 | Model classes. Each method provides specific benefits to Pulp that are outlined here. |
||
217 | |||
218 | Additionally, each of these model inheritance mechanisms can be combined with the other mechanisms |
||
219 | as-needed to create a sound object-oriented design that also generates a reasonable database schema. |
||
220 | |||
221 | Abstract Base classes |
||
222 | \*****\*****\*\*\*\*\*\*\*\*\*\*\*\*\* |
||
223 | |||
224 | https://docs.djangoproject.com/en/1.8/topics/db/models/#abstract-base-classes |
||
225 | |||
226 | Many of our ContentUnit classes have common fields and/or behavior. Abstract base classes make it |
||
227 | trivial for us to put those common bits of code in a single place, to be inherited by Model classes in |
||
228 | completely standard and pythonic ways, such as with subclassing or mixins. |
||
229 | |||
230 | The many different RPM-like ContentUnit subclasses use this extensively, combining in the |
||
231 | "detail" classes to make the complete unit model for a given type. |
||
232 | |||
233 | Multi-table Inheritance |
||
234 | \*****\*****\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* |
||
235 | |||
236 | https://docs.djangoproject.com/en/1.8/topics/db/models/#multi-table-inheritance |
||
237 | |||
238 | This is probably the most important Model inheritance mechanism, as it is used to implement the |
||
239 | ContentUnit "master-detail" relationship, where the "master" content units contain all fields |
||
240 | common to all (or most) ContentUnits, and the "detail" content units contain all of the type-specific |
||
241 | fields for that unit type. |
||
242 | |||
243 | On the database level, the information representing a ContentUnit resides in at least two database |
||
244 | tables (the master ContentUnit table and the detail table), and is seamlessly joined by Django on |
||
245 | instances of the detail ContentUnit model (e.g. RPM, a puppet Module, etc). |
||
246 | |||
247 | More information on this is documented later in the section describing ContentUnit changes. |
||
248 | |||
249 | .. note:: |
||
250 | If you go by what wikipedia has to say about master-detail relationships, this isn't quite that. |
||
251 | However, it makes it easy to refer to both sides of the master/detail relationship with terms that |
||
252 | are easy to understand in the context of ContentUnits, so it's worth appropriating those |
||
253 | terms for Pulp's purposes here. |
||
254 | |||
255 | ~~~ |
||
256 | https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface#Data_model |
||
257 | ~~~ |
||
258 | |||
259 | Proxy Models |
||
260 | \*****\*****\*\*\*\* |
||
261 | |||
262 | https://docs.djangoproject.com/en/1.8/topics/db/models/#proxy-models |
||
263 | https://docs.djangoproject.com/en/1.8/topics/db/queries/#backwards-related-objects |
||
264 | |||
265 | Not currently used in Pulp, but mentioned here for docs completeness. |
||
266 | |||
267 | Generic Relations |
||
268 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
269 | |||
270 | https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/#generic-relations |
||
271 | |||
272 | Django's Generic Relations give us the ability to associate many models to one model in a more |
||
273 | flexible was than a normal ForeignKey. Normally used for things like object tagging, |
||
274 | the Generic Relations that come with Django's contenttypes framework can be used by Pulp to easily |
||
275 | associate a generic Django Model with any number of other models that can benefit from storing the |
||
276 | information captured by the generic model. |
||
277 | |||
278 | A good example of this are the various Generic Key/Value stores that can be associated with any other |
||
279 | Model, "Notes" and "Config". |
||
280 | |||
281 | MongoEngine to Django Field Conversions |
||
282 | \--------------------------------------- |
||
283 | |||
284 | These are the MongoEngine field types currently used by Pulp, and guidelines on converting them to |
||
285 | Postgres. Since MongoEngine started out to get mongodb working as a Django backend, most fields have |
||
286 | direct counterparts in Django. The following subsections are the MongoEngine fields currently used in |
||
287 | Pulp, with applicable postgres datatypes and Django field alternatives listed inside. |
||
288 | |||
289 | In each MongoEngine field section there will be a link to that MongoEngine field's documentation, |
||
290 | brief information about the corresponding postgres data type, subsections detailing the Django |
||
291 | field or fields that should be used when translating a given MongoEngine field, and a list of |
||
292 | files in which that MongoEngine field is being used. |
||
293 | |||
294 | StringField |
||
295 | \^<sup>\^\^\^\^\^\^\^</sup>\^ |
||
296 | |||
297 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.StringField |
||
298 | |||
299 | This field can be represented by one of two Django fields, depending on which Postgres column is a |
||
300 | better fit for the data being stored in it. |
||
301 | |||
302 | Postgres datatype reference: |
||
303 | https://www.postgresql.org/docs/current/static/datatype-character.html |
||
304 | |||
305 | For our purposes, only varchar and text are interesting, the character type will be ignored. While |
||
306 | some database engines have differences in performance between the varchar and text data types, |
||
307 | this tip from the linked postgres docs is good to keep in mind: |
||
308 | |||
309 | .. note:: |
||
310 | "There are no performance differences between these three types, apart from the increased storage size |
||
311 | when using the blank-padded type. While character(n) has performance advantages in some other database |
||
312 | systems, it has no such advantages in PostgreSQL. In most situations text or character varying should |
||
313 | be used instead." |
||
314 | |||
315 | The "blank-padded" type mentioned in that quote is the character type, so for our purposes there is no |
||
316 | difference in performance between varchar and text. |
||
317 | |||
318 | Used in: |
||
319 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
320 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/fields.py\` |
||
321 | \- \`pulp_ostree/plugins/pulp_ostree/plugins/db/model.py\` |
||
322 | \- \`pulp_docker/plugins/pulp_docker/plugins/models.py\` |
||
323 | \- \`pulp_puppet/pulp_puppet_plugins/pulp_puppet/plugins/db/models.py\` |
||
324 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
325 | \- \`pulp/server/pulp/server/db/fields.py\` |
||
326 | \- \`pulp_python/plugins/pulp_python/plugins/models.py\` |
||
327 | |||
328 | CharField |
||
329 | \*****\*****\* |
||
330 | |||
331 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#charfield |
||
332 | |||
333 | Represented by a varchar field in postgres, the max_length argument is required. |
||
334 | |||
335 | When the maximum length of a string is known, such as when storing hash values of a known type (or |
||
336 | types), this is the field to use. String length validation is done at the database level. |
||
337 | |||
338 | TextField |
||
339 | \*****\*****\* |
||
340 | |||
341 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#textfield |
||
342 | |||
343 | Represented by a text field in postgres. |
||
344 | |||
345 | When the maximum length of a string is unknown, such as when storing large chunks of text like errata |
||
346 | descriptions/summaries, this is the field to use. |
||
347 | |||
348 | IntField |
||
349 | \^<sup>\^\^\^\^</sup>\^ |
||
350 | |||
351 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.IntField |
||
352 | |||
353 | There are more numeric types supported by postgres + Django than are offered by MongoEngine, |
||
354 | so converting from one of these MongoEngine fields to a postgres field should take |
||
355 | the available Django field types into account to ensure that the most appropriate |
||
356 | postgres data type is being used. |
||
357 | |||
358 | https://www.postgresql.org/docs/current/static/datatype-numeric.html |
||
359 | |||
360 | The only known MongoEngine FloatField in Pulp is a timestamp field on the Distribution document, |
||
361 | which could reasonably be converted to a DateTimeField. |
||
362 | |||
363 | Used in: |
||
364 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
365 | \- \`pulp_docker/plugins/pulp_docker/plugins/models.py\` |
||
366 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
367 | |||
368 | IntegerField, SmallIntegerField, BigIntegerField |
||
369 | \*****\*****\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* |
||
370 | |||
371 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#integerfield |
||
372 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#smallintegerfield |
||
373 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#bigintegerfield |
||
374 | |||
375 | 2-byte, 4-byte, and 8-byte (respectively) storage for signed integers. |
||
376 | |||
377 | PositiveIntegerField, PositiveSmallIntegerField |
||
378 | \*****\*****\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* |
||
379 | |||
380 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#positiveintegerfield |
||
381 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#positivesmallintegerfield |
||
382 | |||
383 | Positive-only variants of SmallIntegerField and IntegerField. These use the |
||
384 | same postgres data types as their non-"Positive" counterparts, but use database |
||
385 | validation to enforce values \>= 0. |
||
386 | |||
387 | FloatField |
||
388 | \^<sup>\^\^\^\^\^\^</sup>\^ |
||
389 | |||
390 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.FloatField |
||
391 | |||
392 | Also numeric types, just like IntField and LongField, but there are some python representation options |
||
393 | when it comes to floats that are available in django fields. |
||
394 | |||
395 | https://www.postgresql.org/docs/current/static/datatype-numeric.html |
||
396 | |||
397 | Used in: |
||
398 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
399 | |||
400 | FloatField |
||
401 | \*****\*****\*\* |
||
402 | |||
403 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#floatfield |
||
404 | |||
405 | Stored as the "double precision" data type, using 8 bytes of storage. Represents the python "float" |
||
406 | type. |
||
407 | |||
408 | DecimalField |
||
409 | \*****\*****\*\*\*\* |
||
410 | |||
411 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#decimalfield |
||
412 | |||
413 | Stored as the "numeric" data type, storage size varies based on the field precision declared when the |
||
414 | field is created. Very similar to FloatField, but values are represented by the python |
||
415 | "decimal.Decimal" type. Use this field instead of FloatField in cases where the "decimal.Decimal" |
||
416 | type is more appropriate. |
||
417 | |||
418 | For reference: https://docs.python.org/3/library/decimal.html |
||
419 | |||
420 | The postgres docs state that "The actual storage requirement is two bytes for each group of four |
||
421 | decimal digits, plus three to eight bytes overhead," so there's no obvious storage efficiency benefit |
||
422 | the be gained by using this field. |
||
423 | |||
424 | BooleanField |
||
425 | \^<sup>\^\^\^\^\^\^\^\^</sup>\^ |
||
426 | |||
427 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.BooleanField |
||
428 | |||
429 | A normal BooleanField, represented a True/False value in python. |
||
430 | |||
431 | https://www.postgresql.org/docs/current/static/datatype-boolean.html |
||
432 | |||
433 | Used in: |
||
434 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
435 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
436 | |||
437 | BooleanField, NullBooleanField |
||
438 | \*****\*****\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* |
||
439 | |||
440 | Represented by the "boolean" data type in postgres. "BooleanField" stores only True or False, |
||
441 | and cannot be null/None, so a default must be specified. The "NullBooleanField" alternative |
||
442 | additionally allows for null/None values, useful in cases where a boolean value might be |
||
443 | unknown, or not required. |
||
444 | |||
445 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#booleanfield |
||
446 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#nullbooleanfield |
||
447 | |||
448 | DateTimeField, UTCDateTimeField, ISO8601StringField |
||
449 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
450 | |||
451 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.DateTimeField |
||
452 | |||
453 | All mongoengine DateTimeFields should, at this point, be storing UTC datetime |
||
454 | stamps, represented in python as "datetime.datetime" instances. UTCDateTimeField and |
||
455 | ISO8601StringField are custom fields with special behavior for storage, but |
||
456 | all datetimes should be stored in postgres as postgres's native data type, so the only |
||
457 | Django field type we should be using for all of these mongo fields is DateTimeField. |
||
458 | Custom serialization/deserialization of datetime data should be done at the API layer. |
||
459 | |||
460 | https://www.postgresql.org/docs/current/static/datatype-datetime.html |
||
461 | |||
462 | Used in: |
||
463 | \- \`pulp_ostree/plugins/pulp_ostree/plugins/db/model.py\` |
||
464 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
465 | \- \`pulp/server/pulp/server/db/fields.py\` |
||
466 | |||
467 | DateTimeField |
||
468 | \*****\*****\*\*\*\*\* |
||
469 | |||
470 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#datetimefield |
||
471 | |||
472 | Represented in postgres as the "timestamp with time zone" data type. Django is configured |
||
473 | to use the UTC timezone, so tz-aware datetime objects will be properly converted to |
||
474 | UTC timestamps when stored, our custom UTCDateTimeField is not required with Django. |
||
475 | |||
476 | DateField, TimeField |
||
477 | \^<sup>\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^</sup>\^ |
||
478 | |||
479 | MongoEngine does not provide equivalents for these field types, but they're worth mentioning |
||
480 | in the event that only a date or time component of a datetime object needs to be stored. |
||
481 | |||
482 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#datefield |
||
483 | |||
484 | DateField represents the postgres "date" data type, and is the "datetime.date" type in python. |
||
485 | |||
486 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#timefield |
||
487 | |||
488 | TimeField represents the postgres "time" data type, and is the "datetime.time" type in python. |
||
489 | Unlike DateTimeField, TimeField appears to be unaware of time zones; the column type is |
||
490 | "time with |
||
491 | |||
492 | UUIDField |
||
493 | \^<sup>\^\^\^\^\^</sup>\^ |
||
494 | |||
495 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.UUIDField |
||
496 | |||
497 | UUIDs, represented by instances of the "uuid.UUID" data type. |
||
498 | |||
499 | Used in: |
||
500 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
501 | |||
502 | UUIDField |
||
503 | \*****\*****\* |
||
504 | |||
505 | https://docs.djangoproject.com/en/1.8/ref/models/fields/#uuidfield |
||
506 | |||
507 | Postgres has native support for UUIDs with the "uuid" data type, storing the value |
||
508 | as the UUID's 128-bit/16-byte value, rather than the UUID string representation. |
||
509 | |||
510 | All models in Pulp 3 also use a UUIDField as their Primary Key by default. |
||
511 | |||
512 | ListField |
||
513 | \^<sup>\^\^\^\^\^</sup>\^ |
||
514 | |||
515 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.ListField |
||
516 | |||
517 | In general, elements of ListField arrays should be turned into their own |
||
518 | Django Model, with a ForeignKey relationship back to the Model that originally |
||
519 | contained the ListField. |
||
520 | |||
521 | A sort of case-study regarding converting ListFields to models can be found in the |
||
522 | "ListField Conversion Example" section of this document. |
||
523 | |||
524 | Used in: |
||
525 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
526 | \- \`pulp_docker/plugins/pulp_docker/plugins/models.py\` |
||
527 | \- \`pulp_puppet/pulp_puppet_plugins/pulp_puppet/plugins/db/models.py\` |
||
528 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
529 | |||
530 | DictField |
||
531 | \^<sup>\^\^\^\^\^</sup>\^ |
||
532 | |||
533 | http://docs.mongoengine.org/apireference.html#mongoengine.fields.DictField |
||
534 | |||
535 | There are many and varied instances of DictFields in Pulp. DictFields can usually |
||
536 | either be reduced to key/value stores, or should (like with ListField) be turned |
||
537 | into Django Models that ForeignKey back to the Model that originally contained the |
||
538 | DictField. For the case of key/value stores, see the "Arbitrary User Data" section |
||
539 | for details on how to handle that case. |
||
540 | |||
541 | Used in: |
||
542 | \- \`pulp_rpm/plugins/pulp_rpm/plugins/db/models.py\` |
||
543 | \- \`pulp_ostree/plugins/pulp_ostree/plugins/db/model.py\` |
||
544 | \- \`pulp/server/pulp/server/db/model/\_\_init\_\_.py\` |
||
545 | |||
546 | UUID Primary Keys |
||
547 | \----------------- |
||
548 | |||
549 | Postgres has native support for the UUID datatype, as does Django, making a UUID a viable option |
||
550 | for primary keys. UUIDs are already being used at the de-facto Primary Key of the MongoEngine |
||
551 | ContentUnit. Keeping these UUIDs when migrating to Postgres makes it so that users integrating with |
||
552 | Pulp will be able to keep any references they may have in their own data stores to Pulp ContentUnit |
||
553 | by their existing UUID PK. |
||
554 | |||
555 | Master and Detail ContentUnit Types |
||
556 | \----------------------------------- |
||
557 | |||
558 | The "master" ContentUnit model (ContentUnit itself) has some special behaviors added to accomodate |
||
559 | the master-detail inheritance implementation. ContentUnit instance have a \`cast\` method that will |
||
560 | return a "detail" instance of a ContentUnit type, e.g. the RPM instance for that ContentUnit. Calling |
||
561 | \`cast\` on a detail instance will return that instance, making \`cast\` idempotent. |
||
562 | |||
563 | Similarly, all ContentUnits have a \`content_unit\` property that, when accessed, will always be the |
||
564 | master ContentUnit instance. It functions similarly to \`cast\`, in that it is idempotent. This is a |
||
565 | property, not a method, because all detail ContentUnit instances are already ContentUnits in an |
||
566 | object-oriented sense, whereas \`cast\`-ing ContentUnits will most likely result in a database JOIN |
||
567 | operation. |
||
568 | |||
569 | ListField Conversion Example (Errata) |
||
570 | \------------------------------------- |
||
571 | |||
572 | In Pulp 2, the Errata model has many ListFields associated with it: |
||
573 | \- references, a list of items to which this Errata refers, such as BZ bugs and CVEs |
||
574 | \- pkglist, a list of package collections (themselves a list) referred to by this errata |
||
575 | |||
576 | As a result, both "references" and "pkglist" should become their own Model with a corresponding table |
||
577 | in the database with a ForeignKey relationship back to Errata. Furthermore, because the "pkglist" |
||
578 | element in updateinfo.xml can contains package collections, another Model is needed to represent |
||
579 | those package collections, which then has a ForeignKey relationship back to the pkglist that contains |
||
580 | it. |
||
581 | |||
582 | To sum up, the single Pulp 2 Errata model, with its two ListFields, becomes four Django Models: |
||
583 | \- Errata |
||
584 | |||
585 | ~~~ |
||
586 | - ErrataReference - Exposed on Errata instances at the "references" attribute |
||
587 | - ErrataCollection - Exposed on Errata instances as the "pkglist" attribute |
||
588 | ~~~ |
||
589 | |||
590 | ~~~ |
||
591 | - ErrataPackage - Exposed on ErrataCollection instances as the "packages" attribute |
||
592 | ~~~ |
||
593 | |||
594 | These models (probably!) meet the requirements for errata: |
||
595 | \- Pulp can store all data found in errata updateinfo XML files when syncing repos. |
||
596 | \- Pulp can generate equivalent updateinfo XML files when publishing repos. |