I need to check if the fields already exist. If that field doesn't exist, then I need to create a new one. Like the availability check on a booking system
My Model
# Choices
time = [
('1', 'Morning'),
('2', 'Afternoon'),
('3', 'Evening'),
('4', 'Night'),
]
# Model
class Calender(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL,on_delete=models.SET_NULL)
date = models.DateField()
time = models.CharField(max_length=10, choices=time)
location = models.CharField(max_length=32)
My View
class CheckAvailabilityAPIView(APIView):
def post(self, request):
date = request.data.get('date')
time = request.data.get('time')
location = request.data.get('location')
calender = Calender.objects.all()
for obj in calender:
if obj.booked_date == date and obj.time == time and obj.location == location:
return Response({'response': 'Already exist'})
else:
user_id = request.user.id
user = User.objects.get(id=user_id)
serializer = VendorsCalenderSerializer(data=request.data)
if serializer.is_valid():
serializer.save(
user=user,
date=date,
time=time,
location=location
)
return Response({'response': 'Success', 'result': serializer.data})
else:
return Response({'response': 'Failed', 'error': serializer.errors},
status=status.HTTP_400_BAD_REQUEST)
Can someone help me with this.?
You can try using get_or_create()
Something like this :
appointment, created = Calender.objects.get_or_create(
user=user,
date=date,
time=time,
location=location
)
Returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created.
Then based on created you can return customised response.
Reference : https://docs.djangoproject.com/en/2.2/ref/models/querysets/#get-or-create
And maybe their is a typo in Calender. Maybe you meant Calendar.
change calender = Calender.objects.all()
to
calender, result = Calender.objects.get_or_create(booked_date= date, section_timing = time, location = location)
if result is True, then new calender is created
You can also use filter :
calender = session.query(Calender).filter_by(**kwargs).first()
if calender:
return Response({'response': 'Already exist'})
else:
Related
I'm trying custom setting model to change some settings from admin.
class Setting(models.Model):
is_duel_cart_allowed = models.BooleanField(default=True)
free_cancellation_duration = models.DurationField(default="0:0:0")
return_order_duration = models.DurationField(default="0:0:0")
is_return_charges_deducted = models.BooleanField(_("Return charges applied when order is returned"),default=True)
is_shipping_charges_deducted_on_return = models.BooleanField(_("Deduct shipping charges when order is returned"),default=True)
is_product_cancellation_charge_deducted = models.BooleanField(_("Cancellation Charges deducted when product is cancelled"), default=True)
is_shipping_charges_deducted_on_cancel = models.BooleanField(_("Deduct shipping charges when order is cancelled"), default=True)
This is my model and when I turn ON/OFF any of these boolean fields, settings will remain unchanged until server is restarted.
How to tackle this problem?
Here is my view function code where I'm checking for these models changes
from core.models import Setting
setting = Setting.objects.first()
class ReturnOrderViewset(viewsets.ModelViewSet):
serializer_class = OrderSerializer
def get_queryset(self):
queryset = Order.objects.prefetch_related('items', 'items__product', 'items__user').filter(status_id=12)
return queryset
def create(self, request, *args, **kwargs):
order = request.POST.get('order_id')
return_reason = request.POST.get('return_reason')
current_time = timezone.now()
return_order_duration = setting.return_order_duration
try:
order = Order.objects.get(id=int(order))
return_reason = ReturnReason.objects.get(id=int(return_reason))
except Order.DoesNotExist:
return Response({'message': 'Order not found'}, status=status.HTTP_404_NOT_FOUND)
except ReturnReason.DoesNotExist:
return Response({'message': 'Order not found'}, status=status.HTTP_404_NOT_FOUND)
exp_time = order.created + return_order_duration
print(order.created, exp_time)
if order.status_id == 8:
print("status is complete ======")
if exp_time > current_time:
print("exp time is great than current time")
if all(item.product.is_return_allowed for item in order.items.all()):
print("All products are return allowed =====")
deductions = 0.0
if setting.is_return_charges_deducted:
deductions += float(return_reason.return_charges)
if setting.is_shipping_charges_deducted_on_return:
deductions += float(order.shipping_charges)
returning_amount = float(order.total_cost) - float(deductions)
order.status_id = 12
order.return_reason=return_reason
order.save()
subject = "Order Return Email"
message = render_to_string('order_return.html', {'order': order})
recipient_list = [order.user.email,]
send_html_email(subject, message, recipient_list)
return Response({
'message': 'Your order is returned',
'deductions': deductions,
'returning_amount': round(returning_amount, 2)},
status=status.HTTP_200_OK
)
else:
print("some products return not allowed =====")
return Response({'message': '[ERR RNA] Your order cannot be returned, please contact customer support'}, status=status.HTTP_200_OK)
else:
print("more than return time =====")
return Response({'message': '[ERR TMEXD] Your order cannot be returned, please contact customer support'}, status=status.HTTP_200_OK)
else:
print('Order status is not complete')
return Response({'message': '[ERR SNCOM] Your order cannot be returned, please contact customer support'}, status=status.HTTP_200_OK)
Even after changing setting from admin the calculations are done on previous settings. And once I restart server then its fine. How to tackle this in production?
Thanks In Advance
first i update my model instance, after that i tried to insert a new data but showing
"IntegrityError('duplicate key value violates unique constraint "RFIDActivation_ActivationId_key"\nDETAIL: Key ("ActivationId")=(6de9ed9a) already exists.\n',)"
Models.py
class RFIDActivation(models.Model):
RFIDActivationId = models.AutoField(primary_key=True, db_column='RFIDActivationId')
Device = models.ForeignKey(Device, on_delete=models.CASCADE, db_column='DeviceId')
Employee = models.ForeignKey(Employee, on_delete=models.CASCADE, db_column='EmployeeId')
ActivationId = models.CharField(max_length=10, unique=True, default=uuid4().hex[:8])
ActivationStatus = models.CharField(max_length=1)default=None)
class Meta:
db_table = "RFIDActivation"
my serializer.py
class RFIDActivationSerializer(serializers.ModelSerializer):
class Meta:
model = RFIDActivation
fields = '__all__'
view.py
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_activation_initial(request):
RFIDActivation.objects.filter(Employee=request.POST.get("Employee")).update(
ActivationStatus='2',
ActivationMessage='Abort'
)
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
but rfid_serializer.save() shows exceptionexception
Previous ActivationId is using to saving the new data. ActivationId is unique and auto generated. How can i solve this. Error is showing trying to insert after update query
view.py for working fine code only insertion.
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_data_add(request):
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
In restframework, serializer.save() has different behavior based on creation and update. If you pass instance to serializer, it will update the object, otherwise, it will create a new one
#api_view(['POST', 'PUT'])
def rfid_data(request, pk):
"""
Update or delete a rfid data.
"""
rfid_activation = {
...
}
if request.method == 'POST':
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data,status=status.HTTP_201_CREATED)
return return Response(rfid_serializer.data, status=status.HTTP_400_BAD_REQUEST)
# PUT is used for object update
elif request.method == 'PUT':
rfid = RFIDActivation.objects.get(pk=pk)
# pass instance to your serializer
# pass partial=True to allow partial updates
rfid_serializer = RFIDActivationSerializer(instance=rfid, data=rfid_activation, partial=True)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data)
return Response(rfid_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
UPDATE
default is calculated at import time, in your case, your default value for activation_id is calculated once and never change for subsequent instances.
You have to define a function, thus default callable will be updated each time on object creation, read more here
def activation_id_generator(self):
return uuid4().hex[:8]
# then in your class
class RFIDActivation(models.Model):
...
ActivationId = models.CharField(max_length=10, unique=True, default=activation_id_generator)
How can I maintain the history of a Model as well as its related_fields ??
For Eg:
====Contact====
| name |
===============
====ContactPhone======
| contact <ContactID> |
| number |
=======================
I want to maintain revisions of Contact, even if the ContactPhone changes.
Is there any way? I also want to store which user did that change.
P.S. I know https://django-simple-history.readthedocs.io/en/latest/ but it is for only one model.
I'm having a problem with managing related fields for a revision.
Custom Solution:
There's an Application Level solution, in REST Serializers. Warning: Use If and only if the CRUD is only done using REST API, otherwise there'll be a lot of inconsistencies in your system.
Getting User in Serializers:
In DRF(Django REST Framework), We've Serializers and during update, Create, or delete operations, we can get user in serializers (using context).
eg:
class Ser(serilalizers.Serializer):
def some_function(self):
request = self.context.get('request')
user = request.user
super(Ser,self).some_function()
Using this, we can fetch the user.
Handling nested changes:
So, Here Person is the Contact in the question.
class PersonSerializer(WritableNestedModelSerializer):
links = PersonContactLinkSerializer(many=True)
phones = PersonContactNumberSerializer(many=True)
emails = PersonContactEmailSerializer(many=True)
relation_with = PersonRelationSerializer(many=True)
workprofile = PersonWorkProfileSerializerPerson(many=True)
class Meta:
model = Person
fields = '__all__'
def update(self, instance, validated_data):
updatable_fields = ['suffix', 'prefix', 'first_name', 'middle_name', 'last_name', 'date_of_birth', 'marital_status',
'religion', 'gender', 'nationality', 'origin_country', 'citizen_of', 'resident_status', 'addresses', 'address_text']
self.update_actions = []
for field, value in validated_data.items():
if not field in updatable_fields:
continue
new_value = value
old_value = getattr(instance, field)
if str(old_value) != str(new_value):
action = "UPDATE"
if len(str(new_value)) == 0 or new_value == None:
action = "DELETE"
self.update_actions.append({
'action': action,
'field': field,
'old_value': str(old_value),
'new_value': str(new_value),
'entity_type': 'PERSON',
'entity': "Person ID: <{}>".format(instance.id)
})
self.context['update_actions'] = self.update_actions
person = super(PersonSerializer, self).update(instance, validated_data)
if len(self.update_actions):
by_user = self.context['request'].user.crm_user
ContactHistory.save_update_action(
by_user=by_user, contact=instance.contact, actions=self.update_actions)
return person
and ContactPhone is PersonContactNumber.
class PersonContactNumberSerializer(serializers.ModelSerializer):
class Meta:
model = PersonContactNumber
exclude = ('person',)
def update(self, instance, validated_data):
update_actions = self.context.get('update_actions')
make_own_history = False
if update_actions == None:
update_actions = []
make_own_history = True
updatable_fields = ['phone', 'label', 'country_code']
for field, value in validated_data.items():
if not field in updatable_fields:
continue
new_value = value
old_value = getattr(instance, field)
if str(old_value) != str(new_value):
action = "UPDATE"
if len(str(new_value)) == 0 or new_value == None:
action = "DELETE"
update_actions.append({
'action': action,
'field': field,
'old_value': str(old_value),
'new_value': str(new_value),
'entity_type': 'PERSON_CONTACT_NUMBER',
'entity': "PersonContactNumber Object ID: <{}>".format(instance.id)
})
if make_own_history:
user = self.context['request'].user.crm_user
ContactHistory.save_update_action(
by_user=user,
actions=update_actions,
contact=instance.person.contact
)
return super(PersonContactNumberSerializer, self).update(instance, validated_data)
def create(self, *args, **kwargs):
instance = super(PersonContactNumberSerializer,
self).create(*args, **kwargs)
update_actions = self.context.get('update_actions')
make_own_history = False
if update_actions == None:
update_actions = []
make_own_history = True
updatable_fields = ['phone', 'label', 'country_code']
for field in updatable_fields:
new_value = getattr(instance, field)
old_value = None
action = "CREATE"
update_actions.append({
'action': action,
'field': field,
'old_value': str(old_value),
'new_value': str(new_value),
'entity_type': 'PERSON_CONTACT_NUMBER',
'entity': "PersonContactNumber Object ID: <{}>".format(instance.id)
})
if make_own_history:
user = self.context['request'].user.crm_user
ContactHistory.save_update_action(
by_user=user,
actions=update_actions,
contact=instance.person.contact
)
return instance
This Creates the history in a nested form, where parent has control over child's changes, We're getting child's changes in parent using context.
P.S. You're free to define own Data Structure for handling changes, This was my case, I want to save it like that, but,
you can always define the more specific data structure, where child's changes are encapsulated into one key and you're controlling them in parent after child's computation.
In my case, I'm just accessing the child's changes.
P.S. You can create a mixin to check redundancy.
Thank you
I have two models like this:
class Sector(models.Model):
name = models.CharField(max_length=100, db_index=True, unique=True) # HERE IF I REMOVE unique=True, it works correctly
class Address(models.Model):
...
sector = models.ForeignKey(Sector, null=True, blank=True)
And a serializer for the Address model:
In the view, I have this:
address_serialized = AddressSerializer(data=request.data)
if address_serialized.is_valid():
address_serialized.save(client=client)
It never gets to the create function. I have a serialized with a create function that looks like this:
class AddressSerializer(serializers.ModelSerializer):
city_gps = CitySerializer(required=False)
sector = SectorSerializer(required=False)
class Meta:
model = Address
fields = (..., "sector")
def create(self, validated_data):
...
sector_dict = validated_data.get("sector", None)
sector = None
if sector_dict and "name" in sector_dict and city_gps:
if Sector.objects.filter(name=sector_dict["name"], city=city_gps).exists():
sector = Sector.objects.get(name=sector_dict["name"], city=city_gps)
# pdb.set_trace()
if "sector" in validated_data:
validated_data.pop("sector")
if "city_gps" in validated_data:
validated_data.pop("city_gps")
address = Address.objects.create(sector=sector, city_gps=city_gps, **validated_data)
return address
The code never touches this function, is_valid() returns False. And the message is
{"sector":{"name":["sector with this name already exists."]}}
I need to be able to create a new address with FK to the already existing sector. How can I achieve that? Any advice will help.
EDIT
The view looks like this:
class ClientProfileAddressCreateView(APIView):
# throttle_scope = '1persecond'
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
def post(self, request):
try:
client = Client.objects.get(user=request.user)
except ObjectDoesNotExist:
return Response({"error": "A client profile for the logged user does not exit"},
status=status.HTTP_404_NOT_FOUND)
address_serialized = AddressSerializer(data=request.data)
print("address_serialized.is_valid: %s" % address_serialized.is_valid()) # Returns False when unique=True in models
if address_serialized.is_valid():
# print("address_serialized: %s" % address_serialized.data)
address_serialized.save(client=client)
else:
return Response(data=address_serialized.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(data=address_serialized.data, status=status.HTTP_201_CREATED)
This is a known issue with nested serializers and unique constraints.
Really awesome thing to always do is actually print the Serializer - that can give you a lot of extra info.
When you have a json like this:
{
"Sector": {
"name": "Sector XYZ"
},
"address_line_one": “Some Random Address”
}
Django REST framework does not know whether you're creating or getting the Sector object, thus it forces validation on every request.
What you need to do is the following:
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
Then to handle validation you would need to redo the create/update part to fit the uniqueness constraint and raise exception/validation error.
I hope this helps.
Helpful links: This SO Answer and Dealing with unique constraints in nested serializers
EDIT :
As per cezar's request: I will add how it might look like to override the create method of the serializer. I have not tried this code, but the logic goes like this.
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
def create(self, validated_data):
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model
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)
# FIELD CHECK
your_field = validated_data.get("your_field","") # or validated_data["your_field"]
try:
YourModel.objects.filter(your_check=your_field).get()
raise ValidationError("Your error")
except YourModel.DoesNotExist:
# if it doesn't exist it means that no model containing that field exists so pass it. You can use YourQuerySet.exists() but then the logic changes
pass
try:
instance = ModelClass.objects.create(**validated_data)
except TypeError:
tb = traceback.format_exc()
msg = (
'Got a `TypeError` when calling `%s.objects.create()`. '
'This may be because you have a writable field on the '
'serializer class that is not a valid argument to '
'`%s.objects.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.__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
I have models.py
class Visit(Model):
reference_visit = models.ForeignKey('self',
help_text="Visit needs a refrence to Prior Visits",
null=True, blank=True)
show_prior_responses = models.BooleanField(default=False,
help_text="Show PriorResponses")
# has many field but i am making it short.
def __unicode__(self):
result = """Visit id:%s pt:%s""" % (self.id, self.patient.id)
return result
forms.py
class VisitSetupForm(Form):
list_visit_ids = ModelChoiceField(
queryset=Visit.objects.none(),
empty_label='Select Revisit ID',required=False)
show_prior_visit = ModelChoiceField(
queryset=User.objects.all(),
empty_label="Select User for Revisit",required = False)
has many but question is on list_visit_ids.
views.py
def setup(request):
"""
Allow an Admin user the ability to setup a patient & visit all at once.
"""
if request.user.is_superuser:
form_class = AdminVisitSetupForm
all_topics = True
else:
form_class = VisitSetupForm
all_topics = False
f = form_class()
# Get a list of topics for each report.
report_topics = {}
for r in Interview.objects.all():
report_topics[r.id] = [t['ad'] for t in r.topics.values('ad')]
data = {
'superuser':request.user.is_superuser,
'report_topics':simplejson.dumps(report_topics)
}
try:
request.user.reviewer
data['reviewer'] = True
except:
pass
if request.method == "POST":
f = form_class(request.POST)
if f.is_valid():
# Create the patient, generate a password, and send them on their way.
cd = f.cleaned_data
patient = None
if cd['revisit']:
# Check for an existing user first.
try:
patient = Patient.objects.get(username=cd['username'])
except Patient.DoesNotExist, e:
data['form'] = f
data['msg'] = 'There is no user with this username.'
return render_to_response('visit/setup.html', data, context_instance=RequestContext(request))
admin_user = get_user(request)
organization = None
if admin_user:
organization = admin_user.organization
if patient and not request.user.is_superuser:
# Make sure the patient they've selected is one of their own.
if patient.organization != organization:
return HttpResponseForbidden('You are not allowed to see this page.')
if not patient:
password = generate_password()
user = User.objects.create_user(cd['username'], cd['contact_email'], password)
user.first_name = cd['first_name']
user.last_name = cd['last_name']
user.save()
patient = Patient(
user=user,
username=user.username,
contact_phone=cd['contact_phone'],
date_of_birth=cd['date_of_birth'],
email=user.email,
first_name=user.first_name,
gender=cd['gender'],
last_name=user.last_name,
maiden_name=cd['maiden_name'],
organization=organization,
patient_type=cd['patient_type'],
security_answer=cd['security_answer'],
security_question=cd['security_question'],
)
patient.save()
# Send them an email.
t = loader.get_template('www/new_account.txt')
c = Context({
'password':'%s-%s-%s' % (password[:3], password[3:5], password[5:]),
'patient':patient
})
msg = t.render(c)
try:
send_mail(
'A request by your physician to do an online medical history before your appointment.',
msg,
'support#careprep.com',
[user.email]
)
except Exception, e:
log.error('Could not send email for new account %s because: [%s]' % (user.username, e))
request.session['password'] = password
# Create the Visit, too.
interview = cd['interview']
list_visit_ids = cd['list_visit_ids']
print list_visit_ids
visit = Visit(
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
patient=patient
)
if request.user.is_superuser:
topics = cd['topics']
else:
topics = set(list(interview.topics.all()) + list(cd['topics']))
reviewer_mode = cd.get('reviewer_mode') or patient.patient_type == 'Reviewer'
url, visit = initialize_visit(
request,
patient=patient,
starting_section=interview.starting_section,
visit_title='%s %s' % (patient, interview.title),
topics=topics,
reviewer_mode=reviewer_mode,
chief_complaint=cd['chief_complaint'],
location=cd['interview_site'],
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
)
next_url = "/visit/confirmation/%s/%s/?next=%s" % (patient.user.id, interview.id, url)
else:
v = Visit.objects.get(pk=request.POST['list_visit_ids'])
print v
return HttpResponseRedirect(next_url)
# all the fields that are not given pls ignore.
The template is fine.
Now watch forms.py when i do list_visit_ids = ModelChoiceField(queryset=Visit.objects.all(), empty_label='Select Revisit ID',required=False) It works perfectly fine on my local machine.But on my server it has around 6000 visit objects so this page hangs or i should say keep on loading.
So initially i changed it to list_visit_ids = ModelChoiceField(queryset=Visit.objects.none(), empty_label='Select Revisit ID',required=False)
Now i know that by this the form becomes invalid and should go to the else part Now my question how do i make reference_visit=cd['list_visit_ids'] in else (form is invalid)
case save().How do i override the none() attribute.
Thanks in advance i will really appreciate.
If your goal is to save your html page load by removing the 6000 choices (which I've done too: 10000+ <option> fields wrapped by misc html will absolutely choke a page), you shouldn't be using a ChoiceField at all. By setting queryset=Visit.objects.none() you're allowing zero choices and nothing passed in will validate.
You either show 6000 select item drop downs, radio boxes, etc., or find a way to /not/ have a giant select drop down (such as a hidden input or charfield), not fake around a ModelChoiceField who's main purpose is to populate that select drop down and validate.
In short: don't use a ModelChoiceField if you're not going to be using the html choices generated by it. Use something else and do the validation / model pulling yourself via the clean_FOO methods.
class MyForm(forms.Form):
my_input = forms.CharField()
def clean_my_input(self):
input = self.cleaned_data.get('my_input')
try:
return MyModel.objects.get(pk=input) # add a filter here if you want
# (whatever filters you were using in the queryset argument)
except MyModel.DoesNotExist:
raise forms.ValidationError("Doesn't exist / is invalid")
return input