James, you rock dude. lol

On Thu, Jul 20, 2017 at 2:20 PM, James Schneider <[email protected]>
wrote:

>
>
> On Thu, Jul 20, 2017 at 8:44 AM, <[email protected]> wrote:
>
>> Hey, I've been running into issues trying to figure out how a user could
>> be able to access multiple versions of an app.  Here is the following
>> example:
>>
>> I have an app named ANNA.  ANNA is used to build labels and these labels
>> are dependent upon different standards that come out.  For example, we have
>> standard 1700, standard 1800, standard 1900.  The DjangoProject website has
>> a bottom corner button that allows you to go to different versions of the
>> django docs.  I would like to implement that into ANNA, however I've come
>> across a number of problems.  So ANNA has the following app structure:
>>
>>
> Those buttons refer to different releases of the same application. They
> really are just shortcuts to a folder path that points at the particular
> release:
>
> https://docs.djangoproject.com/en/1.7/
> https://docs.djangoproject.com/en/1.11/
> https://docs.djangoproject.com/en/dev/
>
> I haven't investigated, but I'm guessing that each of those URL paths map
> back to a directory containing a full release of the Django docs at the
> appropriate version.
>
> my_project
>>      - ANNA
>>      - build    (development)
>>      - search (development)
>>
>> I have tried to implement this versioning by the following partition
>>
>> *PARTITION 1*
>>
>> my_project
>>     - ANNA
>>     - build   (development)
>>     - search (development)
>>     - build1700
>>     - search1700
>>     - build1800
>>     - search1800
>>     - build1900
>>     - search1900
>>
>> Unfortunately, the models used in all versions of build are the same
>> across the board, and Django's reverse lookup for these models is giving me
>> issues.  For instance if I have a label model in build, I will also have a
>> label model in build1700, build1800, etc.  So I have to change the model
>> names for each version, which means I have to change all of the business
>> code to be version specific -- and I really do not want to do that since
>> our label standards are also in development and change quite rapidly (like
>> every four months right now).  The versions are also similar enough to
>> where when a new one comes out, I would like to be able to pretty much copy
>> and paste the last version's code into the new version's code and make my
>> changes like so.
>>
>>
> Has anyone ever had to do anything like this or have any ideas of how to
>> go about it?
>>
>
> Disclaimer: I'm obviously not familiar with your application or workflow,
> so take this with a grain of salt. My assumption is that you are creating
> output for something like shipping labels that have to follow particular
> formatting standards. If not, clarity via a brief overview of what your app
> does would be helpful.
>
> The abbreviated version of what I have below is "I highly doubt you need
> separate apps, but rather a single app with a better model design to
> support multiple label/build types."
>
>
> It appears you are creating a separate application for each label type. In
> doing so, I'm assuming that you are copying the code from another "version"
> (presumably development) and then modifying the entire application
> accordingly. From what I gather, the work being performed in each "version"
> is very similar with only a few minor differences (standard number, label
> size, address placement, font used, etc.). If that is the case, the
> versioning scheme you've developed is very inefficient and difficult to
> maintain.
>
> I would recommend that you examine the workflow processes from a much
> broader view. For example, to print a label, you would need to know it's
> total size, type (water resistant, semi-gloss, etc.), printable area, and
> so on. Presumably all of your labels have common attributes like these
> (don't think about specific values at this point).
>
> Gather all of those common attributes for handy reference. In addition,
> generate a list of unique attributes specific to a label type (perhaps a
> built-in RFID tag ID that only higher-end labels have or something like
> that).
>
> Now the fun/tricky part. There are two design patterns I see to address
> your design.
>
> First pattern:
>
> Generate an abstract model that contains all of the common attributes that
> most or all of your labels will have. This could be things like standard
> number, size, weight, etc. You would then create regular models that
> inherit from your new abstract model. Each of these models would either
> contain no new fields (if everything they need is contained in the abstract
> model), or would add the label-specific fields in. Things like the standard
> number can be made available via class-level attributes (ie standard_number
> = 1700) or via a common function defined in the abstract model that each
> regular model can override if necessary. Here's how I would handle the
> standard number:
>
> class AbstractLabel(models.Model):
>
>     _standard_number = None
>
>     size_horizontal = models.FloatField...
>     size_vertical = models.FloatField...
>     ....other fields
>
>     def get_standard_number(self):
>         return getattr(self, '_standard_number', None)
>
>     # You can also use a property
>     @property
>     def standard_number(self):
>         return getattr(self, '_standard_number', None)
>
>     @standard_number.setter
>     def standard_number(self, value):
>         raise ValueError('You should not be setting this dynamically!')
>
>     @standard_number.deleter
>     def standard_number(self):
>         raise ValueError('You should not be deleting this dynamically!')
>
>     class Meta:
>         abstract = True
>
> class Label1700(AbstractLabel):
>     _standard_number = 1700
>
> class Label1800(AbstractLabel):
>     _standard_number = 1800
>
> class LabelBlah(AbstractLabel):
>     def get_standard_number(self):
>         return "I'm a blah label."
>
> This would generate a separate table for each label type, and provide a
> common function between them to retrieve the standard number if you need it:
>
> >>> my_label = Label1700.objects.get()
> >>> print(my_label.get_standard_number())
> 1700
>
> You can add other methods to your label models that perform tasks (the
> models are usually where business logic belongs) such as adding an address,
> displaying the label, or generating and returning a PDF of the label based
> on the current label data. I would highly recommend you come up with a
> standard API for your label models. For example, all of your label models
> should support the get_standard_number() method, so it doesn't matter which
> model type you are working with, you can always get the model number the
> same way.
>
> Where this pattern begins to fall apart is determining when to reference
> each type of label. You'll likely need code to determine what type of label
> you need, and then call that class dynamically.
>
> >>> standard = 1900
> >>> import importlib
> >>> LabelModel = getattr(importlib.import_module("module.submodule"),
> "Label{}".format(standard))
> >>> label_instance = LabelModel().objects.get()
> >>> print(label_instance.get_standard_number())
> 1900
>
> It's not the worst thing in the world to abstract away the actual model
> class you are working with, and helps ensure that the business logic
> methods stay generic and work regardless of the standard number. You can
> also create generic methods for generating HTML, etc. that can be used in
> templates, so that you can use a single template with any of your label
> models. Granted, this overlaps your business logic with design, but is
> usually acceptable when used sparingly and with a high-degree of
> abstraction.
>
> Pros: Each model class has its own table, and is easier to query for if
> you know what specific model type you are looking for. Business logic is
> either abstracted away in the abstract parent model, or can be customized
> for each individual label type directly in the concrete model class.
>
> Drawbacks: System-wide reporting is often an issue. A query for 'all' of
> the labels in the system requires that you know all of the label standards
> ahead of time, and query each model class individually.
>
>
> Second pattern:
>
> Create a model that contains all of the possible attributes for all
> labels. This provides a single monolithic Label() class to work with. Some
> standard numbers may only use some of the attributes, so make sure to have
> sane defaults for the more esoteric attributes.
>
> class Label(AbstractLabel):
>     pass
>
> The magic for this pattern is the split between the model class (which
> contains the values for your model like standard number, etc.) and the
> business logic. You would write a series of functions (or a separate class,
> or series of classes) that would handle the business logic for your single
> Label() model. In addition, the standard number in Label() would be stored
> in the database, rather than held as a class-level attribute. For example:
>
> def get_label_depth(label):
>     if label.label_type != '3d':
>         # Our label is not 3-dimensional, so it has no depth...
>         return None
>
>     return label.depth
>
> >>> my_label = Label.objects.get(standard_number=1700)
> >>> my_label.standard_number
> 1700
> >>> get_label_depth(my_label)
> None
>
> The function should be able to handle any Label() object, regardless of
> the standard number, etc.
>
>
>
> My personal preference would be the first design pattern.
>
> Phew, this is longer than I anticipated, almost there...
>
>
> Now, to address the versioning, once we've abstracted away the label types
> using one of the two patterns above, we now need to determine which label
> type a user will use. The very simple answer is to either create a custom
> user object and add the label type as an attribute directly to the user, or
> create user profiles and have the label type as one of the attributes of
> the user profile.
>
> In your case, it sounds like particular users will always have a
> particular label type assigned, and that the label type will be needed for
> most, if not all, requests. This would make it a good candidate for an
> attribute directly on a custom user object. How you use it will depend on
> the design pattern, but you'll have the necessary information to act
> accordingly to whatever label type the user needs. You'll have a single
> code base, and will be postured to easily handle more label types if they
> become necessary, and updates to label standards can be implemented with a
> small amount of work in most cases.
>
> You may run in to trouble with old labels if you need to keep them in
> their original version, but the standard changes for new labels going
> forward.
>
>
>>
>> Also, below is my current callback.
>>
>>
>> Unhandled exception in thread started by <function wrapper at 0x38639b0>
>>> Traceback (most recent call last):
>>>   File "/usr/lib64/python2.7/site-packages/django/utils/autoreload.py",
>>> line 227, in wrapper
>>>     fn(*args, **kwargs)
>>>   File 
>>> "/usr/lib64/python2.7/site-packages/django/core/management/commands/runserver.py",
>>> line 125, in inner_run
>>>     self.check(display_num_errors=True)
>>>   File "/usr/lib64/python2.7/site-packages/django/core/management/base.py",
>>> line 405, in check
>>>     raise SystemCheckError(msg)
>>> django.core.management.base.SystemCheckError: SystemCheckError: System
>>> check identified some issues:
>>>
>>> ERRORS:
>>> build1700.Bundle.user: (fields.E304) Reverse accessor for 'Bundle.user'
>>> clashes with reverse accessor for 'Bundle.user'.
>>>     HINT: Add or change a related_name argument to the definition for
>>> 'Bundle.user' or 'Bundle.user'.
>>> build.Bundle.user: (fields.E304) Reverse accessor for 'Bundle.user'
>>> clashes with reverse accessor for 'Bundle.user'.
>>>     HINT: Add or change a related_name argument to the definition for
>>> 'Bundle.user' or 'Bundle.user'.
>>>
>>>
> This is an issue with a model M2M relation. Sounds like you need to define
> a unique related_name attribute.
>
>
>
> I'll shut up now. ;-)
>
> -James
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit https://groups.google.com/d/
> msgid/django-users/CA%2Be%2BciU%3Dukwrm0Fre%3DyW0qSACoWdYJcDq8rN9CZQ9P9Ypu
> aPZA%40mail.gmail.com
> <https://groups.google.com/d/msgid/django-users/CA%2Be%2BciU%3Dukwrm0Fre%3DyW0qSACoWdYJcDq8rN9CZQ9P9YpuaPZA%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
>
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/CAAuoY6NVH83-yRL5g7JNgNSCN3_6rjzAfcS3foWriNMPUoCvsQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to