wq / django-natural-keys Goto Github PK
View Code? Open in Web Editor NEWEnhanced support for natural keys in Django and Django REST Framework.
License: MIT License
Enhanced support for natural keys in Django and Django REST Framework.
License: MIT License
The str() representation of a datetime value is s.isoformat(sep=' ') whereas the representation of a DateTimeField is s.isoformat(sep='T').
It would be nice to have the option for natural_key representation of datetime values using the latter... perhaps adding a datetime_separator variable similar to the natural_key_separator variable. Example attached.
thanks
I am getting the following field when trying to run this code:
"NotImplementedError: Updating an existing natural key is not supported."
However, I am not trying to update the natural key.
Below is my model:
class User(BaseModel):
"""Stores the information associated with a User
first_name - The user's first name.
middle_name - The user's middle name.
last_name - The user's last name.
title - The user's official title.
eauth_id - The user's FSIS eAuth ID.
email - The user's email.
desk_phone - The user's office phone.
desk_phone_extension - The user's office phone extension.
mobile_phone - The user's personal cell phone.
home_phone - The user's home phone.
fsis_cell_phone - The user's FSIS cell phone.
status - The user's activity status.
last_login_time - The timestamp for the last successful login.
access_level - Foreign Key to ListItem Access Level.
sec_user - Foreign Key to security User.
"""
first_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=100, null=True, blank=True)
last_name = models.CharField(max_length=100)
title = models.CharField(max_length=100, null=True, blank=True)
eauth_id = models.CharField(max_length=255, unique=True)
email = models.EmailField(max_length=255)
desk_phone = models.CharField(max_length=12, null=True, blank=True)
desk_phone_extension = models.CharField(
max_length=12, null=True, blank=True)
mobile_phone = models.CharField(max_length=12, null=True, blank=True)
home_phone = models.CharField(max_length=12, null=True, blank=True)
fsis_cell_phone = models.CharField(max_length=12, null=True, blank=True)
last_login_time = models.DateTimeField(
null=True, blank=True)
status = models.ForeignKey(
ListItem, related_name='user_status', on_delete=models.CASCADE)
access_level = models.ForeignKey(
ListItem, related_name='access_level', on_delete=models.CASCADE)
sec_user = models.ForeignKey(
SecUser, related_name='frio_user', on_delete=models.CASCADE)
def __str__(self):
return '%s %s - %s' % (self.first_name, self.last_name, self.email)</code>
Here is my serializer:
class UserSerializer(NaturalKeySerializer):
"""Validates and serializes the User model.
Fields:
* id
* url
* created_time
* last_updated
* eauth_id
* first_name
* middle_name
* last_name
* title
* email
* desk_phone
* desk_phone_extension
* mobile_phone
* fsis_cell_phone
* status_id
* home_phone
* status_name
* status_display_text
* last_login_time
* access_level_id
* access_level_name
* access_level_display_text
"""
# The phone group requires this not_required_char to work.
not_required = {'allow_null': True, 'required': False}
not_required_char = not_required.copy()
not_required_char.update({'allow_blank': True})
phone_regex = r"^\(?[2-9]\d\d\)?-\d{3}-\d{4}$"
desk_phone = serializers.RegexField(regex=phone_regex, **not_required_char)
mobile_phone = serializers.RegexField(
regex=phone_regex, **not_required_char)
home_phone = serializers.RegexField(regex=phone_regex, **not_required_char)
fsis_cell_phone = serializers.RegexField(
regex=phone_regex, **not_required_char)
status_id = serializers.IntegerField(required=False)
status_name = StatusLookupField()
status_display_text = serializers.CharField(
source='status.display_text', read_only=True)
access_level_id = serializers.IntegerField(required=False)
access_level_name = AccessLevelField(required=True)
access_level_display_text = serializers.CharField(
source='access_level.display_text', read_only=True, required=False)
sec_user = serializers.PrimaryKeyRelatedField(read_only=True)
def validate(self, data):
phone_fields = (
'desk_phone',
'mobile_phone',
'home_phone',
'fsis_cell_phone',
)
if self.instance:
new_data = model_to_dict(self.instance)
else:
new_data = {}
new_data.update(data)
if all((new_data.get(f, '') == '' or new_data.get(f, '') is None)
for f in phone_fields):
raise serializers.ValidationError(
'At least one phone is required.')
if not (new_data.get('status') or new_data.get('status_id')):
raise serializers.ValidationError(
'status_id or status_name is required.')
return data
def create(self, validated_data):
self._link_sec_user(validated_data)
return super(UserSerializer, self).create(validated_data)
def _link_sec_user(self, validated_data):
"""Gets or creates a SecUser with the provided eauth_id and sets it on
the FRIO user's sec_user field."""
eauth_id = validated_data.get('eauth_id')
if eauth_id:
module = USDAModule.objects.get(name='FRIO')
sec_user, created = SecUser.objects.get_or_create(
module=module, user_name=eauth_id)
validated_data['sec_user'] = sec_user
def update(self, instance, validated_data):
self._update_sec_user(instance, validated_data)
return super(UserSerializer, self).update(instance, validated_data)
def _update_sec_user(self, instance, validated_data):
"""Updates the security user's user_name to match the eauth_id."""
eauth_id = validated_data.get('eauth_id')
if eauth_id and instance.sec_user.user_name != eauth_id:
instance.sec_user.user_name = eauth_id
instance.sec_user.save()
class Meta:
model = models.User
fields = ('id', 'url', 'created_time', 'last_updated',
'eauth_id', 'first_name', 'middle_name', 'last_name',
'title', 'email', 'desk_phone', 'desk_phone_extension',
'mobile_phone', 'home_phone', 'fsis_cell_phone', 'status_id',
'status_name', 'status_display_text',
'last_login_time', 'access_level_id', 'access_level_name',
'access_level_display_text',
'sec_user'
)</code>
And the code that I am trying to run that is breaking is:
user = client.frio.users.update(user['id'], {'last_login_time': cur_time})
As you can see, I am trying to update 'last_login_time' and the natural_key on that model is eauth_id. Looking at the code it seems that update
is just not implemented in your code. Am I missing something?
Hi!
Today I upgraded my dev instance to Django 2.0 and after (of course) fixing a few things, I'm having a problem with django-natural-keys.
Exactly:
django-natural-keys/natural_keys/models.py
Line 129 in 6594a64
For whatever reason CharObject (and probably others as well) do no longer provide the attribute 'rel'.
I've changed the code for the moment, that it looks this way:
rel_to = None
if hasattr(field, 'rel'):
rel_to = field.rel.to if field.rel else None
But I'm not 100 % confident, that this is the (safe) way to go.
See also: AttributeError: model object has no attribute 'rel' @ Stackoverflow
Thx for
Requiring that a model inherit from NaturalKeyModel can conflict with other model modifiers that also requiring inheritance. It would be nice if the same functionality could be exposed via a class decorator. E.g.,
@NaturallyKeyedModel
class MyModel(SomeOtherModelSuperclass):
# ...
Django 2.2 introduces Unique Constraint meta option which provides greater flexibility in defining uniqueness constrains. Currently django-natural-keys does not consider Unique Constraints. According to latest (3.0) Django documentation for unique_together meta option:
Use UniqueConstraint with the constraints option instead.
UniqueConstraint provides more functionality than unique_together. unique_together may be deprecated in the future.
Therefore NaturalKeyModel should be updated to check for Unique Constraints to define natural key fields
Hi, I had a problem importing FK:s with null value. I added this code below to my class to work around.
I have a feeling though that this is not entierly correct if the Null appears on a different level??
(Ex: It fails when re-importing an existing record, calls constructor with field_id-values but constructor looks for field. Seems like constructor should never have been called.)
def natural_key(self):
"Override implementation to handle null refs"
# Recursively extract properties from related objects if needed
vals = [reduce(getattr, name_list, self) if getattr(self, name_list[0]) is not None else None
for name_list in [name.split('__') for name in self.get_natural_key_fields()]]
return vals
Orig impl in models.py line 215:
def natural_key(self):
"""
Return the natural key for this object.
(This is a generic implementation of the standard Django function)
"""
# Recursively extract properties from related objects if needed
vals = [reduce(getattr, name.split('__'), self) # Will call reduce on None
for name in self.get_natural_key_fields()]
return vals
From Django 3.2 new models are now generated by default with BigAutoField
IDs instead of AutoField
, as written in the release notes.
This breaks the support for natural keys when using a single unique
field in the model, because this check inside get_natural_key_def()
now fails.
I can write a PR which checks for BigAutoField
too, but I don't know if someone can come up with a more generalized method.
Hi. With 1.4.0 I'm getting following error:
AttributeError: 'Q' object has no attribute 'split'
Traceback:
File "/Users/dexter/src/gui/project/admin.py", line 179, in changelist_view
return super().changelist_view(request, extra_context)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/utils/decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/utils/decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/contrib/admin/options.py", line 1542, in changelist_view
self.list_max_show_all, self.list_editable, self,
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/contrib/admin/views/main.py", line 78, in __init__
self.queryset = self.get_queryset(request)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/contrib/admin/views/main.py", line 351, in get_queryset
qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query)
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/django/contrib/admin/options.py", line 910, in get_search_results
queryset = queryset.filter(reduce(operator.or_, or_queries))
File "/Users/dexter/src/gui/.virtualenv/lib/python3.6/site-packages/natural_keys/models.py", line 8, in filter
slugs = natural_key_slug.split(
AttributeError: 'Q' object has no attribute 'split'
>>> natural_key_slug
<Q: (OR: ('id__iexact', '9'), ('name__istartswith', '9'))>
It was after I tried to use "Search" field in list view.
Hello,
I'm discovering django-natural-keys.
It seems djangorestframework is required for loaddata/dumpdata operations.
Does it make sense to add djangorestframework as django-natural-keys dependencies ?
Best regards
I often user natural key in my models' repr, so adding this here would be nice, something like:
'<%s: %s>' % (self._meta.model.__name__, ', '.join(self.natural_key()))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.