1.要在drf实现用户数据的操作我们需要定义相关的serializer类,serializer实现对User的操作
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model=User
fields=["username","password","email"]
- 接下来我们需要定义相应的view这里我们采用CBV的方式
class UserView(APIView):
def get(self,request):
users=User.objects.all()
serializer=UserSerializer(users,many=True)
return Response(serializer.data)
def post(self,request):
serializer=UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
上面的操作实现都很简单,下面才是问题的关键,我们不妨来测试一下我们写好的接口,结果如下图
这里的用户ll_admin是我一开始使用createsuperuser命令创建的一个超级用户,这里我们可以看到的是这里接口展示的是用户名以及加密的用户密码,那如果此时我们 如果通过接口post方法来添加一个用户会产生什么结果呢
我们post了一个用户名是admin,密码是1234的用户,此时我们用get方法重新获取一下数据看看会怎样
这里我们可以看到我们post过去的数据产生的用户他的密码是没有加密的 ,那么这是为什么,我们不妨来看一下源码
class UserView(APIView):
def get(self,request):
users=User.objects.all()
serializer=UserSerializer(users,many=True)
return Response(serializer.data)
def post(self,request):
serializer=UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
我们首先来看一下上面方法中的post方法,首先我们使用post过来的 数据来填充serializer,如果serializer有效则 将serializer保存,那么这个serializer.save()就是问题的关键,我们 可以在rest_framework中的serializers模块中查看相应的源码,看看这个save方法的具体实现过程
def save(self, **kwargs):
assert not hasattr(self, 'save_object'), (
'Serializer `%s.%s` has old-style version 2 `.save_object()` '
'that is no longer compatible with REST framework 3. '
'Use the new-style `.create()` and `.update()` methods instead.' %
(self.__class__.__module__, self.__class__.__name__)
)
assert hasattr(self, '_errors'), (
'You must call `.is_valid()` before calling `.save()`.'
)
assert not self.errors, (
'You cannot call `.save()` on a serializer with invalid data.'
)
# Guard against incorrect use of `serializer.save(commit=False)`
assert 'commit' not in kwargs, (
"'commit' is not a valid keyword argument to the 'save()' method. "
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
"You can also pass additional keyword arguments to 'save()' if you "
"need to set extra attributes on the saved model instance. "
"For example: 'serializer.save(owner=request.user)'.'"
)
assert not hasattr(self, '_data'), (
"You cannot call `.save()` after accessing `serializer.data`."
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
)
validated_data = dict(
list(self.validated_data.items()) +
list(kwargs.items())
)
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
return self.instance
我们这里直接从if self.instance is not None:这里看起,如果serializer.instance不为空就调用本身的update方法,如果为空我们就调用本身的create方法,那么我们再来看看create方法到底是怎样执行的
def create(self, validated_data):
"""
We have a bit of extra checking around this in order to provide
descriptive messages when something goes wrong, but this method is
essentially just:
return ExampleModel.objects.create(**validated_data)
If there are many to many fields present on the instance then they
cannot be set until the model is instantiated, in which case the
implementation is like so:
example_relationship = validated_data.pop('example_relationship')
instance = ExampleModel.objects.create(**validated_data)
instance.example_relationship = example_relationship
return instance
The default implementation also does not handle nested relationships.
If you want to support writable nested relationships you'll need
to write an explicit `.create()` method.
"""
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model
# Remove many-to-many relationships from validated_data.
# They are not valid arguments to the default `.create()` method,
# as they require that the instance has already been saved.
info = model_meta.get_field_info(ModelClass)
many_to_many = {}
for field_name, relation_info in info.relations.items():
if relation_info.to_many and (field_name in validated_data):
many_to_many[field_name] = validated_data.pop(field_name)
try:
instance = ModelClass._default_manager.create(**validated_data)
except TypeError:
tb = traceback.format_exc()
msg = (
'Got a `TypeError` when calling `%s.%s.create()`. '
'This may be because you have a writable field on the '
'serializer class that is not a valid argument to '
'`%s.%s.create()`. You may need to make the field '
'read-only, or override the %s.create() method to handle '
'this correctly.\nOriginal exception was:\n %s' %
(
ModelClass.__name__,
ModelClass._default_manager.name,
ModelClass.__name__,
ModelClass._default_manager.name,
self.__class__.__name__,
tb
)
)
raise TypeError(msg)
# Save many-to-many relationships after the instance is created.
if many_to_many:
for field_name, value in many_to_many.items():
field = getattr(instance, field_name)
field.set(value)
return instance
我们可以看try:
instance = ModelClass._default_manager.create(**validated_data)这句,这句这里会用会创建一个modelclass实例,而这个modelclass就是我们在自己定义的UserSerializer中定义的model,也就说我们自己定义的UserView中的serializer.save()方法相当于就是执行了了User.objects.create()方法,所以我们解决添加用户时密码不能加密的方法就是在UserSerializer中重构create方法最后我们的UserSerializer类如下
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model=User
fields=["username","password","email"]
def create(self, validated_data):
user=User(**validated_data)
user.set_password(validated_data["password"])
user.save()
return user
这里我们先实例化一个user,在将其保存到数据库之前调用set_password方法对密码进行加密,这样我们再来测试我们的接口。
我们post一个用户名是xadmin,密码是12345的用户,我们可以再次get刷新一下看看结果
我们看到新添加的用户密码已经加密,至此已经大功告成