Is adding attributes dynamically frowned upon in Python? - python

In Python, you can assign an arbitrary attribute from outside the defining class:
class Profile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=140)
p = Profile()
p.age = 42
The underlying mechanism here is __dict__ attribute that maintains a dictionary of all attributes.
We were all told not to expose our inner workings to the client code, but attaching new data doesn't have to do with encapsulation at all, right? Is this idiom common for Python code?
Just What I Mean…
Each Tweet has standard fields, like id, text, owner.
When returning tweet list for a user, you want to display if a tweet is “favorited” by this user.
Obviously, to obtain is_favorite you need to query many-to-many relationship for this user.
Would it be OK to pre-fill Tweet objects with is_favorite corresponding to current user?
Sure I could expose a method is_favorite_for(user) but I'm hitting Django template language limitations that doesn't allow to call methods with arguments from inside the template. Also, I believe a template should not be calling methods at all.
I know this will work fine, but I wonder if doing something like that in an open source project would get other developers to look on me with contempt.
Sidenote:
I come from C#/.NET background where dynamic types were introduced very recently and aren't adapted widely except for some niche areas (interoperability, IoC frameworks, REST client frameworks, etc).

My view is that it is a bad practice.
The object doesn't know that you're messing with its attributes. Consider, for example, what would happen if Profile were later expanded to have an attribute called age, unrelated to p.age in your code.
If you want to add attributes, why not subclass Profile, or have an external mapping of Profiles to an object with your custom attributes?

I think the answer is: It depends. First, if you really want to prevent it you can by defining __slots__ in the class. And it is not generally a good practice to add attributes not actually defined in the class, as it can be confusing to someone reading
the code and is rarely useful.
But at certain times, it is useful to be able to do this and Python documentation discusses this as a way to get something similar to a C struct or Pascal Record (see http://docs.python.org/tutorial/classes.html under section 9.7 Odds and Ends.)

If the attribute is only there sometimes, you risk getting an AttributeError out of nowhere for one object while the code worked fine for another object of the same class (yes, exact types aren't that important when duck-typing, but objects of the same class are frequently assumed to be of the same "duck type"). Even if it doesn't happen, you can't be sure just by looking at part of the code, and it's much harder to check in any case. So, doing this only makes your code less reliable.
Then there's the option of providing a default attribute as class attribute or property, only assigning an object attribute when it differs from the default. But for stuff that is expected to vary per object, the clarity of having every attribute ever listed in __init__ usually outweights any potential advantages of delaying instance attribute access.
That is not to say it's not acceptable, but you'd have to make a compelling argument for it to be considered a good idea.

Related

Flask-Login current_user changing to the most recent User class query result

My implementation of Flask-Login is working well across my site, with the exception of contexts where I need to make additional queries on the User class. For example, when I try to generate a list of User instances representing other users with whom the current_user shares a task, the values stored in current_user are rewritten each time a new instance of User is created. In addition, the attributes of each of the other previously-generated User instances are also overwritten, with the exception of the User.id attribute, which remains unique (and correct).
I am using pyAirtable as an ORM between Flask and an Airtable base containing user data. The ORM is experimental and built using abstract base classes. I wonder whether this issue may be due to a conflict between instance and class variables.
I specifically suspect the _fields variable may be the culprit, because it appears as both a class variable and an instance variable in the source for the base Model. (The issue may also be with the from_id() method, which I use in a list comprehension to generate the aforementioned list of User instances from a list of Airtable record IDs.) However, I'm not experienced enough with ABCs or Python to be able to determine this for sure. I've tried fiddling with the pyairtable package for debugging, to no avail.
What am I missing? Is this an issue with pyairtable, flask-login, an unexpected interaction between the two packages, or just my implementation?

Most Pythonic double dispatch for extracting View information from Model

I'm coding a desktop app with Python and Qt, using PySide. I need to display a tree view in which top-level items are objects of different type than their children. Specifically, a top-level item is a Git repository, whereas its children are directories in the work tree.
For a repository, I want to show its path and currently checked-out branch. For a directory, I just want to show its name.
Right now, I do this by having my QAbstractItemModel descendant use isinstance on the underlying model object (retrieved from internalPointer() method) and decide how to format the resulting string.
I was wondering whether there was a more Pythonic (or just less clunky) way of doing this kind of double dispatch.
What I don't want to do is define a method for this purpose in my model classes for Git repo and work tree file, because I feel this would violate SRP.
Any thoughts or ideas are most welcome. Also, if anyone can think of a less clunky title for this question, let me know ;)
If you were ok with each Model class having a function containing View code, then you could just call those functions. To separate the Model/View code without using isinstance, this sounds like a case for the Visitor pattern, as described in this SO answer, and as used in the ast module.
Basically, each Model class has an accept() method that takes a Visitor object. The accept() method for a repository calls the visit_repository() method of that Visitor object, passing self (which is the Model instance). Similarly, the accept() method for a directory calls the visit_directory() method of that Visitor object, passing self. The visit_repository() or visit_directory() method then has access to the Model instance and knows its type and can show the appropriate view. This separates the View code (in the Visitor object) from the Model code (in the Model class).
*Note: instead of using different function names (visit_repository() vs visit_directory()), you can use multimethods, e.g., this SO answer about multimethods in Python.

Is it safe to access ._meta directly in your django app?

Django uses Meta class for lots of additional object information. However, they store this information in an object '_meta' which by naming convention is private.
All over the django admin (and other places) I see stuff like opts = model._meta and then they use the various options like app_label and verbose_name.
Can I be confident accessing ._meta and be sure that it will not change in the future, or am I better off creating one 'accessor' mixin or something that accesses the ._meta in one spot, so if it ever does change I only have to update one thing?
I use _meta frequently and haven't had any issues so far. You can see in the django documentation an example of them using it too here, so I have felt its more or less ok to do. Just tread carefully and write good tests so you know of any problems when you upgrade your django version later on down the road.
I use _meta in several projects where I want to have generic access to information that's otherwise not provided by the api. I think you're probably okay most of the time as Django is pretty stable. It's probably a good idea to be covering your usage of _meta in your unit tests.

Python / Django: Is it wise to use "Item" as a class name in Python (in Django in this case)?

I'm creating this:
# models.py
class Item(models.Model):
sku = models.CharField(max_length=20)
class Attribute(models.Model):
item = models.ForeignKey(Item, related_name='items')
Is that going to cause naming collisions in Python? Like:
# views.py
some_object.items.create(sku='123abc')
# Is there a place / way that this could cause errors, like:
# AttributeError: Bound method 'items' has no attribute "create"
# Since items() on a dict-like object could be a method to return a list,
# and Django could add support for .items() on a queryset right?
If it's a bad idea, I could change the name.
It does seem a bit generic, but no more so than "Attribute". I would give it a prefix based on the app if possible.
A model isn't the same thing as a queryset, and neither of them is documented as being dict-like. There shouldn't be any problem with doing this.
If you are really worried, then make as much of this code public as possible, and get people using it :) If nothing else, the Django core team is really diligent about checking as much "in-the-wild" code as they can before extending the documented APIs in any way. They really do care about not breaking people's existing code, as much as possible.
If making it public isn't an option, then at least watch the mailing lists, so that when somebody proposes "Hey, let's add an .items method to Model!", you can at least chime in with a "that'll break my code" at the right time.
It's really not a problem. If you have some random object and feel 'items' is a suitable name for a method, then go ahead. It is not going to cause any collisions with names that happen to be used on other objects.
As long as you think the method name is not misleading and does not cause confusion, go ahead.
go with default item_set, problem-free :)

Does Django support model classes that inherit after many non-abstract models?

Lets say I have three django model classes - lets call them A, B and C. If A and B are abstract, I can do something like:
class C(A,B):
pass
What if they aren't abstract and I do the same? Will everything still work correctly or no? Or have I got it wrong and this should not be done with abstract models either?
I'm having some issues which I'm attributing to the fact that the answer is probably no, but I'd still prefer to make sure about this if anyone knows :)
The specific use case I had for this is probably better served by Generic Relations (I only recently discovered their existence), so I guess it would be understandable if the Django team made a design decision like this (I can't see many people needing to do this). I'd just like to know for sure what the case is.
Edit 1 (after Dominic's answer)
Interesting... The problem we're having is a structure similar to IMDb (I think IMDb is a bit easier to understand than the topic matter we actually have, so I'll use them as an example). On IMDb they have pages for People and pages for Movies and both People and Movies have their own message boards.
We've ended up connecting message boards to the People and Movies by creating a model called MessageboardOwner (with only one attribute - the id added automatically by Django), which "owns" the message board and People and Movies inherit it. The problem is that our "People" class inherits from two other classes also. The class definition is something like:
class Person(A,B,MessageboardOwner):
Initially this seemed to work out fine, but then today something rather weird happened... I was deleting a Person in the admin and the admin asked the "Are you sure?" question and was showing me what other objects it would have to delete. It was trying to delete two message boards, not one. One of these message boards should have been owned by a Movie, not a Person.
Upon looking at what exactly was in the database, I found that this Person instance was using the same MessageboardOwner instance as the Movie was. When I played around with it, what came out was that the Movie class, which inherited only after MessageboardOwner, seemed to work ok. Saving the Person, however, only created a MessageboardOwner object if one didn't already exist (or possibly overwrote the existing one - I'm not sure). I also found that the id fields inherited from A, B and MessageboardOwner were always equal, which seemed strange to me.
Yes, you can use normal Python multiple-inheritance with models. Bear in mind this warning though:
Just as with Python's subclassing,
it's possible for a Django model to
inherit from multiple parent models.
Keep in mind that normal Python name
resolution rules apply. The first base
class that a particular name (e.g.
Meta) appears in will be the one that
is used; for example, this means that
if multiple parents contain a Meta
class, only the first one is going to
be used, and all others will be
ignored.
Generally, you won't need to inherit
from multiple parents. The main
use-case where this is useful is for
"mix-in" classes: adding a particular
extra field or method to every class
that inherits the mix-in. Try to keep
your inheritance hierarchies as simple
and straightforward as possible so
that you won't have to struggle to
work out where a particular piece of
information is coming from.
From the Django docs.
Generally, multiple inheritance is a bad idea, and there are simpler ways to do things. If you flesh out what problem you're trying to solve a bit more clearly, we might be able to help a bit better.

Categories

Resources