James, you rock dude. lol

On Thu, Jul 20, 2017 at 2:20 PM, James Schneider <jrschneide...@gmail.com>
wrote:

>
>
> On Thu, Jul 20, 2017 at 8:44 AM, <pieceofkayk2...@gmail.com> 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 django-users+unsubscr...@googlegroups.com.
> To post to this group, send email to django-users@googlegroups.com.
> 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 django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
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