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%3DyW0qSACoWdYJcDq8rN9CZQ9P9YpuaPZA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to