Django Create or update with unique together values - python-3.x

I'm trying to make an attendance system in the frontend I retrieve a list of users which the request.user can take attendance of, I'm using CreateAPIView but this won't get me the desired effect as I want the request.user to be able to toggle between Absent, Present, and on_leave even after the entry has been created on the first request
i have seen questions and answers about create or update here but i couldn't use them so any help would be appriciated
this is my view.py
class AttendanceListCreateAPIView(CreateAPIView):
permission_classes = [IsTeacher_Student]
queryset = Attendance.objects.all()
serializer_class = AttendanceSerializer
def post(self, request, *args, **kwargs):
user = request.user
data = request.data
serializer = AttendanceSerializer(data=data)
if serializer.is_valid():
data_user = serializer.validated_data['user']
## validation
return Response({"message": "Something is wrong, maybe you have already taken attendance for this user"},
status=status.HTTP_400_BAD_REQUEST)
my serializer.py
class AttendanceSerializer(serializers.ModelSerializer):
date = serializers.HiddenField(default=timezone.now)
leave_reason = serializers.CharField(required=False, default="")
class Meta:
model = Attendance
fields = ['user', 'presence', 'leave_reason', 'date']
extra_kwargs = {
'user': {'required': True},
'presence': {'required': True},
'leave_reason': {'required': False},
}
validators = [
UniqueForYearValidator(
queryset=Attendance.objects.all(),
field='user',
date_field='date',
message=("You have already taken the attendance")
)
]
def create(self, validated_data):
instance = Attendance.objects.create(
user=validated_data['user'],
presence=validated_data['presence'],
leave_reason=validated_data['leave_reason'],
date=validated_data['date'],
)
instance.save()
return instance
desired effect
the user fk and date are unique together meaning that if the date isn't unique for the user update it if it is create a new entry

Related

Django Rest Framework Field Validation Issue

My subscription view is located inside of UserViewSet. I'm wondering why I'm getting
IntegrityError at /api/users/1/subscribe/
new row for relation "users_subscription" violates check constraint "prevent_self_subscription"
DETAIL: Failing row contains (11, 1, 1).
instead of proper json answer. Somehow SubscriptionSerializer field validation doesnt wish to work. Any thoughts?
models.py
class Subscription(models.Model):
user = models.ForeignKey(
User, related_name='subscriber',
on_delete=models.CASCADE)
author = models.ForeignKey(
User, related_name='subscribing',
on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=('user', 'author'),
name='unique_subscription'
),
models.CheckConstraint(
check=~models.Q(user=models.F('author')),
name='prevent_self_subscription'
)
]
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = models.Subscription
fields = ('author', 'user', )
validators = [
serializers.UniqueTogetherValidator(
queryset=models.Subscription.objects.all(),
fields=['author', 'user', ]
)
]
def create(self, validated_data):
return models.Subscription.objects.create(
user=self.context.get('request').user, **validated_data)
def validate_subscribing(self, value):
if self.context.get('request').user == value:
raise serializers.ValidationError(
'You cant subscribe to yourself!')
return value
views.py
#action(['post'], detail=True)
#permission_classes(permissions.IsAuthenticated)
def subscribe(self, request, *args, **kwargs):
author = get_object_or_404(models.User, id=kwargs['id'])
data = request.data.copy()
data.update({'author': author.id})
serializer = serializers.SubscriptionSerializer(
data=data, context={'request': request})
if request.method == 'POST':
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
status=status.HTTP_201_CREATED,
data=self.get_serializer(author).data)
It was the wrong method name, field name is 'author' and method was validate_subscribing() instead of validate_author().

Custom User Model with multiple Unique id - Django RestFramework

Hi StackOverFlow buddies,
I have created a custom User model for my project and would like to register the Users with Restframework.
I want Custom User model to have 2 unique fields together, for which I followed "Official doc" for unique_together property. It seems to be only taking only 1 field (ie email for my case), as a unique one.
Relevant piece my code until this point looks like this:
PS: Let me know if more info is required.
models.py
class MasterUser(AbstractBaseUser):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,)
firstname = models.CharField(max_length=100,blank=True)
contact = models.CharField(max_length=100, blank=True)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['firstname']
class Meta:
unique_together = (('email', 'contact'),)
serializer.py
class RegisterUserSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only= True)
class Meta:
model = MasterUser
fields = ('firstname', 'password', 'password2', 'email','contact')
extra_kwargs = {
'password': {'write_only': True},
}
def save(self):
account = MasterUser(
email = self.validated_data['email'],
firstname = self.validated_data['firstname'],
contact = self.validated_data['contact'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Password doesnt matches'})
account.set_password(password)
account.save()
return account
views.py
#api_view(['POST'])
def registration_view(request):
if request.method == "POST":
serializer = RegisterUserSerializer(data= request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = "Successfully registered new user!"
else:
data = serializer.errors
return Response(data)
Where am I missing to implement thing?
unique_together means the set of fields you defined should be unique for all Users. If you also want contact to be a unique field when stored in db, you should pass the unique parameter like so:
class MasterUser(AbstractBaseUser):
...
contact = models.CharField(max_length=100, blank=True, unique=True)
...

Django Rest Framework DELETE request responds like a GET request

I'm trying to delete an entry in my data base that is returned by a modelviewset get_queryset. When sending a DELETE request through the DRF web interface and via postman, I receive this response "DELETE /api/remove_self/3 HTTP/1.1" 200 along with the data I am trying to delete. The code that gives this result looks like this:
Models.py
class EventAtendee(models.Model):
"""Lists users atending an event"""
#below connects user profile to event
id = models.AutoField(primary_key=True)
event_id = models.IntegerField(null = True)
user_profile = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
return self.event_id
views.py
class RemoveSelfFromEvent(viewsets.ModelViewSet):
"""Remove Yourself From an Event you were attending"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.EventAtendeeSerializer
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
#user_profile = self.kwargs['user_profile']
event_id = self.kwargs['event_id']
return models.EventAtendee.objects.filter(event_id=event_id, user_profile=self.request.user)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
urls.py
router = DefaultRouter(trailing_slash=False)
router.register('events', views.EventAtendeeViewSet, basename='EventAtendee')
urlpatterns = [
path('remove_self/<event_id>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'list'})),
]
Any help is much appreciated!
You are mapping the method DELETE to list in your urls.
path('remove_self/<event_id>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'list'})),
Correct way to do:
path('remove_self/<pk>', views.RemoveSelfFromEvent.as_view({'get': 'list', 'delete': 'destroy'})),
mapping of various methods:
POST : create
GET : retrieve
PUT : update
PATCH : partial_update
DELETE : destroy

How to implement django admin queryset filter with from extended user model class

I have extended Django default User model (just to save user state_id) class with following model.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,)
state_id = models.IntegerField(blank=False)
def __str__(self):
return self.user.username
And now I want to filter the data by logged user's state_id. I tried to implement queryset filter but it does not do anything.
class VisVisitsAdmin(admin.ModelAdmin):
list_per_page = 10
list_display = ('visit_id','visit_no','user_name','mobile_number','program_name','state_name','district_name','block_name','school_name',)
list_filter = ('date_of_visit',)
def queryset(self, request):
qs = super(VisVisitsAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(state_id=request.user.UserProfileInfo.state_id)
def state_name(self, obj):
if obj.school_program:
return obj.school_program.school.cluster.block.district.state.name_of_state
state_name.short_description = 'state name'
You are trying to fetch the state_id from the userprofileinfo instance related to user object.
Everything seems fine but the instance is available as userprofileinfo and not UserProfileInfo , so change qs as :
qs.filter(state_id=request.user.userprofileinfo.state_id)

make some model field conditionally visible in django

I have two field in my django model they should be editable only if user have selected 'type' as 'Dimention' otherwise they should not be visible to user.
My model is look like this code
from django.db import models
class Configuration(models.Model):
name = models.CharField(max_length=30)
user_defined_name = models.CharField(max_length=50)
FieldTypes = (('aD', 'Dimension'), ('aM', 'Measure'))
type = models.CharField(max_length=11, choices=FieldTypes)
is_key = models.BooleanField(default=False, editable=False)
unit = models.CharField(max_length=30, null=True, blank=True, editable=False)
def __str__(self):
return self.name
I know it is possible by using JavaScript but, I don't want to write html or js myself,Thus Can't use JavaScript for doing this.
A pure Django-only way to achieve this is to simply reset the fields from your ModelForm if type is not equal to Dimension. This will appear like magic/unintended behavior; so be careful of the implementation.
For example (assuming you are using the admin interface: the same is valid for a custom ModelForm View):
class ConfigurationAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
# At this point; the object already has the new values set; we will have to reset as needed
conditional_editable_fields = ['is_key', 'unit']
config_type = form.cleaned_data.get('type')
if config_type != 'aD':
for field in conditional_editable_fields:
if field in form.changed_data: # The value has been changed by the user
setattr(obj, field, form.initial.get(field)) # Set the initial value
self.message_user(request, "Cannot edit field: {}; value has been reset".format(field), messages.WARNING) # Inform the user that we reset the value
return super(ConfigurationAdmin, self).save_mode(request, obj, form, change)
I use similar approach for this.
I it works great
In My admin.py
`
fieldsets = (
(None, {
'fields': ('name', 'user_defined_name', 'type', 'is_active')
}),
('Advanced', {
'classes': ('toggle',),
'fields': ('is_kpi', 'unit'),
})
)
actions = [disable_multiple_column, activate_multiple_column]
class Media:
js = ("jquery.js", "my_code.js",)`
I use that JS file to show and hide .
`$(document).ready(function(){
show_hide();
$('#id_type').change(function(){
show_hide();
});
function show_hide(){
if ($("#id_type").val() == 'aM' ){
$(".module")[1].style.display="block"
}
else{
$(".module")[1].style.display="none"
}
}
});`
And in case use already entered values and then change Choice of type or from some other reason these hidden field still have data. I override the save Method of models.py
`
def save(self, *args, **kwargs):
if self.type != 'aM':
self.is_kpi = False
self.unit = None
super(Configuration, self).save(*args, **kwargs)
`

Resources