I've had a look around but can't seem to find any reference to Peewee supporting enum fields, other than a short mailing list thread with no outcome, and a user contributed recipe for Postgres which is untested. There are also arguments for _not_ using enum.
All the supported Peewee databases have support for enum fields, and a quick look at the source indicates it would be trivial to add it in.
Is there any reason why enum fields have not been added yet? Would you consider accepting a PR for this?
I have not added enums because I personally have always used an IntegerField with a set of defined values. My blog, for instance, stores an entry's status as an integer such that 1=published, 2=draft, 9=deleted.
Python does not natively support an enum data-type until 3.4, either, so there is another reason against it. For Python versions 2.x and less than 3.4, how would Peewee users represent enums in their application code?
Lastly, adding a custom field is pretty trivial and there are docs describing how.
All the supported Peewee databases have support for enum fields, and a quick look at the source indicates it would be trivial to add it in.
SQLite does not natively support enums as far as I know. Yeah you can use a convention, or create a special table and use foreign keys, but I wouldn't go so far as to say SQLite supports enums.
Would you consider accepting a PR for this?
How would you go about implementing this? What would the Python side look like?
I'll just repeat:
Would you consider accepting a PR for this?
How would you go about implementing this? What would the Python side look like?
I have to admit, normally I opt for the same IntegerField approach. The only real advantage to enum seems to be string representation and value enforcement, however I'd argue that the majority of applications _should_ be doing validation in the app code rather than the database (with the exception being applications which enforce everything in the DB directly via stored procedures etc).
Given that even sqlite3 doesn't support enum, and the additional code complexity involved, I think it can be argued that there is little value in peewee supporting it either. I'll close this issue out for now, but at least there is some discussion and reasoning behind not adding it, for anyone who comes up against this in future.
Thank you again for your input, much appreciated
A bit late on this - but here is one option that will convert the values on their way into the db, and back on their way out.
# Define a mapping of database values : output
FRUIT_CHOICES = [(1, "apple"),
(2, "orange"),
(3, "banana")]
class enum_like_field(IntegerField):
def __init__(self, choices, *args, **kwargs):
self.to_db = {v:k for k, v in choices}
self.from_db = {k:v for k, v in choices}
super(IntegerField, self).__init__(*args, **kwargs)
def db_value(self, value):
return self.to_db[value]
def python_value(self, value):
return self.from_db[value]
class my_table(Model):
fruit = enum_like_field(null=False, choices=FRUIT_CHOICES)
This code works good for Python 3.4+ enum for MySQL
class enum_field(CharField):
"""
This class enable a Enum like field for Peewee
"""
def __init__(self, choices, *args, **kwargs):
self.choices = choices
super(CharField, self).__init__(*args, **kwargs)
def db_value(self, value):
return value.name
def python_value(self, value):
return self.choices(value)
class my_table(Model):
class StatusChoices(Enum):
WAITING = 1
READY_TO_SEND = 2
UPLOADING = 3
OCR = 4
DONE = 5
status = enum_like_field(null=False, choices=StatusChoices)
I think there is a mistake in the above snippet, this:
def db_value(self, value):
return value.name
Should be:
def db_value(self, value):
return value.value
I also found some mistake.
The Problem is that the top base class Field has in __init__ set default value for the choices=None so calling the super after setting the instance self to some value will because of this base class reset it to None.
One of the solution can be calling the super before setting the choices or using another member name.
Hey sorry for the silly question but is this available in peewee 3.8.0? I can't find the class EnumField and there is no reference of it inside the Changelog nor the official documentation.
No, EnumField is currently something you'll need to implement.
the above code has some bugs and I created this and this works properly:
class EnumField(IntegerField):
"""
This class enable an Enum like field for Peewee
"""
def __init__(self, choices, *args, **kwargs):
super(IntegerField, self).__init__(*args, **kwargs)
self.choices = choices
def db_value(self, value):
return value.value
def python_value(self, value):
return self.choices(value)
@alistvt which kind of issues? What if my Enum is using strings instead of integers, like this?
class OverclockProfileType(Enum):
DEFAULT = 'default'
OFFSET = 'offset'
Yet another version of the EnumField. This version stores the enum value as CharField but restores the correct Enum type when reading it:
class EnumField(CharField):
"""
This class enable an Enum like field for Peewee
"""
def __init__(self, choices: Callable, *args: Any, **kwargs: Any) -> None:
super(CharField, self).__init__(*args, **kwargs)
self.choices = choices
self.max_length = 255
def db_value(self, value: Any) -> Any:
return value.value
def python_value(self, value: Any) -> Any:
return self.choices(type(list(self.choices)[0].value)(value))
Most helpful comment
This code works good for Python 3.4+ enum for MySQL