Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot run update on a non-natural field? #6

Closed
jellylamp opened this issue Jan 19, 2018 · 1 comment
Closed

Cannot run update on a non-natural field? #6

jellylamp opened this issue Jan 19, 2018 · 1 comment

Comments

@jellylamp
Copy link

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?

@sheppard
Copy link
Member

sheppard commented Mar 1, 2019

Hi, I'm guessing you may have sorted this out already, but for future reference:

NaturalKeySerializer is not particularly well suited for use as a top-level serializer. NaturalKeyModelSerializer is generally more appropriate, but in this particular case you probably could get by with a simple slug field. I have updated the documentation to clarify this usage.

@sheppard sheppard closed this as completed Mar 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants