Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialWelby Obeng
20,340 Pointshow UserMixin and class inheritance work?
class User (UserMixin, Model):
username = CharField(unique=True)
email = CharField(unique=True)
password = CharField(max_length=100)
joined_at = DateTimeField(default=datetime.datetime.now)
is_admin = BooleanField(default=False)
Take a look at the code above....What exactly does UserMixin do to the User class?
What will happen if Model came before UserMix in the code below
In what order does python setup the user object with inheritance:
"Model" (properties and methods) then add "User Mix" (properties and methods) in then add "User"(properties and methods) ?
3 Answers
Nicolas Hampton
44,638 PointsIf you really want to know...the answer can get a little longer, but I was wondering the same thing myself and dig some digging.
Kenneth goes over the UserMixin documentation in the video and explains that what it's doing is pretty simple, but there's a link to the source code, so we might as well take a look at HOW simple...
class UserMixin(object):
'''
This provides default implementations for the methods that Flask-Login
expects user objects to have.
'''
@property
def is_active(self):
return True
@property
def is_authenticated(self):
return True
@property
def is_anonymous(self):
return False
def get_id(self):
try:
return unicode(self.id)
except AttributeError:
raise NotImplementedError('No `id` attribute - override `get_id`')
def __eq__(self, other):
'''
Checks the equality of two `UserMixin` objects using `get_id`.
'''
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
'''
Checks the inequality of two `UserMixin` objects using `get_id`.
'''
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
if sys.version_info[0] != 2: # pragma: no cover
# Python 3 implicitly set __hash__ to None if we override __eq__
# We set it back to its default implementation
__hash__ = object.__hash__
So, on the surface, pretty simple. We know a class can inherit more than one class, so all we're doing when we add this one is adding the methods and properties that belong to the UserMixin class to our model class. They're all pretty simple, once you take a closer look at them. Let's go backwards:
if sys.version_info[0] != 2: # pragma: no cover
# Python 3 implicitly set __hash__ to None if we override __eq__
# We set it back to its default implementation
__hash__ = object.__hash__
python 2 and python 3 handle equality between objects differently, thus this test. Nothing important really...
def __eq__(self, other):
'''
Checks the equality of two `UserMixin` objects using `get_id`.
'''
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
'''
Checks the inequality of two `UserMixin` objects using `get_id`.
'''
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
these two definitions override the magic methods for equality and non-equality on the object. For more info, check out this tutorial on magic methods, pretty useful information. Basically, we're just making sure that when we compare User
objects, we compare them by the user_id
. What user_id
, you might ask...
def get_id(self):
try:
return unicode(self.id)
except AttributeError:
raise NotImplementedError('No `id` attribute - override `get_id`')
So, from what I can tell, this id, in our case, is actually present before any of this UserMixin
implementation. It's the automatically added id field of the database entry, which at this level the class can be fairly certain will be present. If the user_id is not present, we'll get an error asking us to modify the method to retrieve whatever we are using to uniquely identify the User objects. Again, nothing special, just a dependable way for the flask-login module to access each unique User
object.
@property
def is_active(self):
return True
@property
def is_authenticated(self):
return True
@property
def is_anonymous(self):
return False
Do you ever get that urge to make something exceedingly simple very complex? Let's feed that urge now. Each one of these "properties" (more in a second) are referring to special states we actually aren't going to use in this class. That's what Kenneth really goes over in this video. We wont be making anonymous users, but we could override this method to create them if we wanted, and flask-login would know where to look for info needed to implement them. All are users will be authenticated (meaning we aren't going to send a text to someone's phone for registration verification or anything like that), and all users will be active at all times (meaning we wont be suspending accounts or requiring additional hoops besides registration to get an account started). These could be useful in other situations in which we cared, but we really don't. Not today, at least.
What's more fascinating to me here are the @property
decorators wrapping each of these functions. Check out the decorators workshop for more information on these, but the @property
decorator is a builtin python decorator. Really, it's just a function that takes a function as an argument. In this case, @property
takes these functions as arguments and is designed to treat them like namespaced properties on the User object. We could write this a couple different ways to show just how simple this really is...
#e.g. 1 - python decorator special syntax
@property
def is_anonymous(self):
return False
#e.g. 2 - the actual simple concept of a decorator function
# is just a function that wraps another function
# to add functionality
def is_anonymous(self): #we're going to wrap this function in the decorator
return False
is_anonymous = property(is_anonymous) # the decorator is just another function
# which takes a function and returns
# a function that does more
is_anonymous(self) #then we can run the enhanced function
See, nothing special really. Now, what does the @property
do, in particular? Well, from what I can tell, it defines getters, setters, and deleters for the value of the original function, and attaches it as a property of the object. Basically, it makes a property. If you want more of a look,
check it out. It looks like a lot of recursive-curried-magic-knot-tying, but it's actually pretty straight forward if you take the time to read the simple yet confusing low level code.
So, after all that, what is the UserMixin
doing for us here? Saving us time writing boilerplate code, really. Looks like we screwed that up being curious cats here, but whatev's, it was fun. Happy coding!
Nicolas
Nicolas Hampton
44,638 PointsAlso, to answer your second question definitively, UserMixin
overrides the get_id
function on the Model class, so that it returns a string value and not an integer value. If you put UserMixin second, then Model overrides UserMixin, and Flask-Login won't get the string value it's expecting to identify users with, thus breaking.
Yeeka Yau
7,410 PointsFrom what I understand, the UserMixin package will give the User class some pre-built functionality to do with user login tasks that will be described a little later. It basically just saves you from having to code that stuff up yourself.
The UserMixin class just gives you extra features, but it is not the Model class, I guess you can think of the model class as say a person, with a bunch of attributes- Usermixin may give your 'person' a few little extras like say a job or a car, but it is not the person. So you are 'mixing' in some attributes to your 'person' - not the other way around.
As far as technically what would happen if you placed Model first, I have no idea - but I'm not sure you need to worry about that (I suppose you could also just try it and see what happens).
User is just the name of your class - it is the name of your particular instance of Model.