Django - TastyPie无法解析关键字到字段

时间:2013-10-01 20:38:59

标签: python django tastypie

我正在尝试通过POST使用django-tastypie

创建用户资源

型号:

  • 用户配置
  • 用户(来自django.contrib.auth.models)

资源:

  • CreateUserResource
  • UserResource
  • UserProfileResrouce

我一直收到错误

  

无法将关键字“city”解析为字段。选项包括:api_key,date_joined,email,first_name,groups,id,is_active,is_staff,is_superuser,last_login,last_name,logentry,password,profile,rating,user_permissions,username

我是django-tastypie的新手,但在我的代码或django / tastypie代码的跟踪中找不到错误。它为什么这样做? 我的代码基于以下博文:http://psjinx.com/programming/2013/06/07/so-you-want-to-create-users-using-djangotastypie/

提前谢谢大家!!

Models.py

from django.utils.translation import ugettext as gtext

class UserProfile(models.Model):
"""
A Model to store extra information for each user
"""

user = models.OneToOneField(User, related_name='profile')
gender = models.CharField(gtext("gender"), max_length=10)
city = models.CharField(gtext("city"), max_length=20)

def __unicode__(self):
    return self.user.get_full_name()

Api.py

class CreateUserResource(ModelResource):
user = fields.ForeignKey('RecommenuAPI.api.UserResource', 'user', full=True)

class Meta:
    allowed_methods = ['post']
    always_return_data = True
    authentication = Authentication()
    authorization = Authorization()
    queryset = UserProfile.objects.all()
    resource_name = "create_user"
    always_return_data = True

def hydrate(self, bundle):
    """
    Makes sure all required fields in UserProfile
    and User are fulfilled. Custom error message returned if not
    """
    REQUIRED_USER_PROFILE_FIELDS = ( "city", "gender", "user")
    for field in REQUIRED_USER_PROFILE_FIELDS:
        if field not in bundle.data["user"]:
            raise CustomBadRequest(
                code = "missing_key",
                message = "Must provide {missing_key} when creating a user."
                          .format(missing_key=field))

    REQUIRED_USER_FIELDS = ("username", "email", "first_name", "last_name", "raw_password")
    for field in REQUIRED_USER_FIELDS:
        if field not in bundle.data["user"]:
            raise CustomBadRequest(
                code = "missing_key",
                message="Must provide {missing_key} when creating a user."
                        .format(missing_key=field))

    return bundle

def obj_create(self, bundle, **kwargs):
    """
     Creates new user. Checks for existing password
     and email match.
    """
    try:
        email = bundle.data["user"]["email"]
        username = bundle.data["user"]["username"]
        if User.objects.filter(email=email):
            raise CustomBadRequest(
                code = "duplicate_exception",
                message = "That email is already used.")
        if User.objects.filter(username=username):
            raise CustomBadRequest(
                code = "duplicate_exception",
                message = "That username is already used")
    except KeyError as missing_key:
        raise CustomBadRequest(
            code = "missing_key",
            message = "Must provide {missing_key} when creating a user."
                      .format(missing_key=missing_key))

    except User.DoesNotExist:
        pass

    #setting resource_name to 'user_profile' here because we want
    #resource_uri in response to be same as UserProfileResource resource
    self._meta.resource_name = UserProfileResource._meta.resource_name
    return super(CreateUserResource, self).obj_create(bundle, **kwargs)


class UserResource(ModelResource):
    # We need to store raw password in a virtual field because hydrate method
    # is called multiple times depending on if it is a POST/PUT/PATCH request
    raw_password = fields.CharField(attribute=None, readonly=True, null=True, blank=True)

    class Meta:
        # For authentication, allow both basic and api key so that the key
        # can be grabbed, if needed
        authentication = MultiAuthentication(
            BasicAuthentication(),
            ApiKeyAuthentication())
        authorization = Authorization()

        # Because this can be updated nested under the UserProfile, it needed
        # 'put'. No idea why, since patch is supposed to be able to handle
        # partial updates
        allowed_methods = ['get', 'patch', 'put']
        always_return_data = True
        queryset = User.objects.all().select_related("api_key")
        excludes = ['is_active', 'is_staff', 'is_superuser', 'date_joined', 'last_login']

    def authorized_read_list(self, object_list, bundle):
        return object_list.filter(id=bundle.request.user.id).select_related()

    def hydrate(self, bundle):
        if "raw_password" in bundle.data:
            # Pop our raw_password and validate it
            # This will prevent re-validation because hydrate is called multiple times
            # "Cannot resolve keyword 'raw_password' into field." wont occur

            raw_password = bundle.data.pop["raw_password"]

            #Validate password
            if not validate_password(raw_password):
                if len(raw_password) < MINIMUM_PASSWORD_LENGTH:
                    raise CustomBadRequest(
                        code="invalid_password",
                        message=(
                            "Your password should contain at least {length} "
                            "characters.".format(length=
                                                 MINIMUM_PASSWORD_LENGTH)))
                raise CustomBadRequest(
                    code="invalid_password",
                    message=("Your password should contain at least one number"
                             ", one uppercase letter, one special character,"
                             " and no spaces."))
            bundle.data["password"] = make_password(raw_password)
        return bundle

    def dehydrate(self, bundle):
        bundle.data["key"] = bundle.obj.api_key.key

        try:
            #Don't return 'raw_password' in response
            del bundle.data["raw_password"]
        except KeyError:
            pass

        return bundle

class UserProfileResource(ModelResource):
    user = fields.ForeignKey(UserResource, 'user', full=True)

    class Meta:
        # For authentication, allow both basic and api key so that the key
        # can be grabbed, if needed
        authentication = MultiAuthentication(
            BasicAuthentication(),
            ApiKeyAuthentication())
        authorization = Authorization()
        always_return_data = True
        allowed_methods = ['get', 'patch', ]
        detail_allowed_methods = ['get', 'patch', 'put']
        queryset = UserProfile.objects.all()
        resource_name = 'user_profile'

    def authorized_read_list(self, object_list, bundle):
        return object_list.filter(user=bundle.request.user).select_related()

    ## Since there is only one user profile object, call get_detail instead
    def get_list(self, request, **kwargs):
        kwargs["pk"] = request.user.profile.pk
        return super(UserProfileResource, self).get_detail(request, **kwargs)

[编辑]添加痕迹

Traceback (most recent call last):

  File      \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 217, in wrapper
    response = callback(request, *args, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 459, in dispatch_list
    return self.dispatch('list', request, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 491, in dispatch
    response = method(request, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 1357, in post_list
    updated_bundle = self.obj_create(bundle, **self.remove_api_resource_names(kwargs))

  File \"/Users/Carlos/PycharmProjects/RecommenuBackend/RecommenuAPI/api.py\", line 82, in obj_create
    return super(CreateUserResource, self).obj_create(bundle, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 2149, in obj_create
    bundle = self.full_hydrate(bundle)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 909, in full_hydrate
    value = field_object.hydrate(bundle)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/fields.py\", line 737, in hydrate
    return self.build_related_resource(value, request=bundle.request)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/fields.py\", line 661, in build_related_resource
    return self.resource_from_data(self.fk_resource, value, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/fields.py\", line 605, in resource_from_data
    return fk_resource.obj_update(fk_bundle, skip_errors=True, **data)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 2205, in obj_update
    bundle.obj = self.obj_get(bundle=bundle, **lookup_kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/tastypie/resources.py\", line 2125, in obj_get
    object_list = self.get_object_list(bundle.request).filter(**kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/django/db/models/query.py\", line 655, in filter
    return self._filter_or_exclude(False, *args, **kwargs)

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/django/db/models/query.py\", line 673, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))

  File \"/Users/Carlos/.virtualenvs/recommenu/lib/python2.7/site-packages/django/db/models/sql/query.py\", line 1266, in add_q
    can_reuse=us(

1 个答案:

答案 0 :(得分:0)

CreateUserResource提供UserProfile个对象。在此资源user字段中是User对象。 User对象没有city字段。 cityUserProfile字段。

更改您的请求以及dehydrate功能的一部分:

REQUIRED_USER_PROFILE_FIELDS = ( "city", "gender", "user")
for field in REQUIRED_USER_PROFILE_FIELDS:
    if field not in bundle.data: