Django Not Creating Model Objects After Retrieving Data From External Payment API - django

I created an app for table ordering (basically like an ecommerce website) that has 3 models:
class Dish(model.Models):
some other fields()
class Cart(model.Models):
customer = models.ForeignKey(User)
session_key = models.CharField(max_length=40)
dish = models.ForeignKey(Dish)
order = models.ForeignKey(Order)
paid = models.CharField(max_length = 3, default = "No")
some other fields()
class Order(model.Models):
customer = models.ForeignKey(User)
session_key = models.CharField(max_length=40)
some other fields()
All the items that are added to the cart have a default field of "No", that turn to "Yes" once the payment has been done. If the user is authenticated, it populates the customer field for the cart item, otherwise it creates a session key with the following function:
def create_session(request):
session = request.session
if not session.session_key:
session.create()
return session
To process the payment, the application uses Square (similar to stripe). The following view takes care of it:
from square.client import Client
def square_payment(request):
# this view receives the necessary data such as order total and token via a POST request.
# I have omitted these details for the simplicity of the example
if request.method == "POST":
#create a client object to handle payment through Square
client = Client(access_token= "some access token",environment= "production")
body = {}
body['amount_money']['amount'] = "some amount"
# execute payment with the amount specified in body
result = client.payments.create_payment(body = body)
if result.is_success():
if request.user.is_authenticated:
order_object = Order.objects.create(customer = request.user)
Cart.objects.filter(customer = request.user, paid = "No").update(paid = "Yes")
else:
session = create_session(request)
order_object = Order.objects.create(session_key = session.session_key)
Cart.objects.filter(session_key = session.session_key, paid = "No").update(paid = "Yes")
elif result.is_error():
return JsonResponse({"Message":"Error"})
I tested everything many times and seemed to work fine until I tested it at a restaurant and the results that I got were quite mixed. The results were as follow:
#1) Everything worked as expected, the payment got through, the Order object was created and all cart items associated to that order were updated to paid = "Yes".
#2) The payment got through, the order object was created but the cart items were not updated to paid "Yes".
#3) The payment got through, but neither the Order object was created nor the cart items updated to paid = "Yes".
Not entirely sure what the issue is, but I would really appreciate it if someone could point me in the right direction.

Related

Django Webhook creates double database entries

I'm currently using Django to code a webhook application for google Dialogflow.
It was working fine, I was basically done.
For some reason, I now started encountering various randomly appearing problems, one of the worst being the following:
Whenever the webhook executes the user account creation call, it creates a double-database entry, which crashes the program (because my .get suddenly returns multiple elements instead of a single user).
I'm using the following simple models:
# model to create user entries
class TestUser(models.Model):
name = models.CharField(max_length=200)
userID = models.CharField(max_length=12, blank=True)
registrationDate = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
# model to add watched movies to user
class Movie(models.Model):
username = models.ForeignKey(TestUser, on_delete=models.CASCADE)
title = models.CharField(max_length=200, blank=True)
genreID = models.IntegerField (blank=True)
def __str__(self):
return self.title
def list_genre_id(self):
return self.genreID
The part that gets executed in my webhook while the problem occurs should be the following:
if action == "account_creation":
selection = req.get("queryResult").get("parameters").get("account_selection")
if selection == "True":
q = TestUser(name=f"{username}", userID=idgen())
q.save()
userID = TestUser.objects.get(name=f"{username}").userID
fullfillmenttext = {"fulfillment_text": "Alright, I created a new account for you! Would you like to add "
"some of your favorite movies to your account?",
"outputContexts": [
{
"name": f"projects/nextflix-d48b9/agent/sessions/{userID}/contexts/create_add_movies",
"lifespanCount": 1,
"parameters": {
"data": "{}"
}
}]}
This is the simple idgen function I'm using:
def idgen():
y = ''.join(
random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(12))
return y
I'm trying to create this userID as a way of having a unique session ID in the webhook calls for all users. Something seems to mess it up, but I don't have the slightest clue what.
Thank you so much for looking over this!
It seems I was able to fix the issue:
The problem was apparently that I had the lifespan of a previous outputContext set to 2 instead of 1, which resulted in the answer executing the codecell twice for some reason. Man, dialogflow is such a terrible program.

How to display remaining days and hours in django from DateTimeField?

Here I have a simple function for sending leave request and accepting by the admin.This code works for now but I want to add some feature here.For example if the user enter day = 2 which is IntegerField then it get stores into databse then after the leave has been accepted by the function below def accept_leave(request,pk): I want to display the remaining days of leave(Example:1 day 12 hours and 30 sec. remaining to complete leave ).After 2 days completed it should display some message like you leave has been completed.
I got no idea for starting this .How can I do it ?Any help would be great.
Is there any mistake in my approach ?
EDIT: Now I removed the day(Integer Field) and added start_day and end_day as DateTimeField. Now how can I display the remaining days and time of leave after the leave is accepted ?
models.py
class Leave(models.Model):
staff = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,related_name='staff_leave')
organization = models.ForeignKey(Organization,on_delete=models.CASCADE,related_name='staff_leave')
sub = models.CharField(max_length=300)
msg = models.TextField()
start_day = models.DateTimeField()
end_day = models.DateTimeField()
#day = models.IntegerField(default=0)
is_accepted = models.BooleanField(default=False)
is_rejected = models.BooleanField(default=False)
sent_on = models.DateTimeField(auto_now_add=True)
views.py
def send_leave_request(request):
form = MakeLeaveForm()
if request.method == 'POST':
form = MakeLeaveForm(request.POST)
if form.is_valid():
leave_days = form.cleaned_data['day']
org = form.cleaned_data['organization']
start_day = form.cleaned_data['start_day']
end_day = form.cleaned_data['end_day']
diff = end_day - start_day
leave_days = diff.days
print('org',org)
if leave_days > request.user.staff.organization.max_leave_days:
messages.error(request,'Sorry can not be sent.Your leave days is greater than {}.'.format(request.user.staff.organization.max_leave_days))
return redirect('organization:view_leaves')
else:
leave = form.save(commit=False)
leave.staff = request.user
leave.organization = org
leave.save()
return redirect('organization:view_leaves')
return render(request,'organization/send_leaves.html',{'form':form})
def accept_leave(request,pk):
leave = get_object_or_404(Leave, pk=pk)
leave.is_accepted = True
leave.is_rejected = False
leave.day = ?
leave.save()
return redirect('organization:leave_detail',leave.pk)
For your leave request, why don't you store something like :
start_date and end_date
The idea is (ideally) to store only things you can't compute.
Then you can make a python property that computes the number of days between start_date and end_date (a "fget" would be enough for the property). A python property won't be stored in your database but it's not a big deal because you can compute it ! So you don't have to store it.
days = property(fget=_get_days, doc="type: Integer")
That means whenever the "days" attribute of an object "Leave" is accessed, the function "_get_days" is called to retrieve what you want.
If self represents a Leave object and you do print(self.days) it will print the result of _get_days finally.
The "doc" part is just here to indicate your property returns an Integer. It is not mandatory but a good practice in order not to forget it.
Then you must write that method "_get_days" (it must be above your property definition or Python won't know what is "_get_days"
def _get_days(self):
return self.end_date - self.start_date
(something like that, that you convert into an int somehow)
Moreover, for your additional functionality, you must know how much leaves your user can have. Just store that on the user, on your user team or whatever you want.
Then to check if the user has remaining leaves he can take, just browse a queryset with all his accepted leaves and use the property mentioned above.
Then you substract the result to the total number of leaves the user can take.

Django ORM verification of a row in the database

In my site, the buyer search for a product and once he found it he can contact the seller by pressing on a contact button. If the conversation between the two concerning this product exists he should be redirected to the existing conversation, else, we create a new conversation.
The conversation is hence defined by two users and a listing.
When I try to implement the logic, I am not able to verify both conditions of the existance of the conversation, if the listing exists OR the users exists Django returns that the conversation exists. Here is my code:
def create_conversation(request, user_pk1, user_pk2, results_pk):
user1 = get_object_or_404(get_user_model(), pk=user_pk1)
user2 = get_object_or_404(get_user_model(), pk=user_pk2)
results= get_object_or_404(Listing, pk=results_pk)
existing_conversation = Conversation.objects.filter(users__in=[user1, user2]).filter(listing=results).values_list('id', flat=True)
if existing_conversation.exists():
return HttpResponseRedirect(reverse('conversation:conversation_update', kwargs={'pk':existing_conversation[0]}))
else:
conv=Conversation()
conv.save()
conv.listing = results
conv.save()
conv.users.add(*[user1,user2])
return HttpResponseRedirect(reverse('conversation:conversation_update', kwargs={'pk': conv.pk}))
Here is the model of the conversation:
class Conversation(models.Model):
"""
Model to contain different messages between one or more users.
:users: Users participating in this conversation.
:archived_by: List of participants, who archived this conversation.
:notified: List of participants, who have received an email notification.
:unread_by: List of participants, who haven't read this conversation.]\['
listing: the listing the conversation is about.
read_by_all: Date all participants have marked this conversation as read.
"""
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
verbose_name=_('Users'),
related_name='conversations',
)
# review the listing before going online, because it is necessary to have a conversation listing
listing = models.ForeignKey (
Listing,
verbose_name=_('Listing'),
related_name='listing',
default= 1,
)
and the model of the listing:
class Listing(models.Model):
seller = models.ForeignKey(Profile, related_name='seller_listing', verbose_name='sellr', limit_choices_to={'is_seller': True})
location_Country = models.CharField(max_length=45, verbose_name=_('from_Country'))
location_State = models.CharField(max_length=45, verbose_name=_('from_State'), null=True, blank=True)
location_City = models.CharField(max_length=45, verbose_name=_('from_City'))
I also tried an approach of divinding it into two conditions: a = conversation.objects.filter(users) and b= conversation.objects.filter(listing), then use if a and b then the conversation exists but got the same issue.
and existing_conversation = Conversation.objects.filter(Q(users__in=[user1, user2]) & Q(listing=results)).values_list('id', flat=True) but got the same issue. Thank you in advance for your help.
You can use intersection() method of django, added since Django 1.11, operator to return the shared elements of two or more QuerySets or the bitwise operation AND used with the sign `& to get the same behavior.
So in your case, check whether there's an intersection between the two users with & or intersection()
existing_conversation = (user1.conversations.all() & user2.conversations.all()).filter(listing=results)
# or with django intersection
existing_conversation = (user1.conversations.all().intersection(user2.conversations.all())).filter(listing=results)
if existing_conversation.exists():
return HttpResponseRedirect(reverse('conversation:conversation_update',
kwargs={'pk':existing_conversation.first().pk}))
else:
# rest of the code
BONUS, I see a typo in your code, you didn't send the pk as argument:
kwargs={'pk':existing_conversation[0]}
get the first instance with first() and get the pk
kwargs={'pk':existing_conversation.first().pk}
or
kwargs={'pk':existing_conversation[0].pk}

Multiple Form with Single Submit Button

I'm currently working with django project. I had to filter the data store on the database based on the user input on form (at template) as looked below.
On form user either enter value or leave it blank. So what I have to do is first find the (valid) user input and then fire appropriate query to display data as user input in the form. So final result should be displayed on table at template.
As I'm new to django, how should I have to pass the data and fire query to represent data at multiple field. As help or link related to these type problem are expected. ( I just able to filter from the database with only one form and had no concept to solve this.)
Model of my temp project is as below.
class exReporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
gender = models.CharField(max_length=1)
age = models.IntegerField()
label = models.IntegerField()
There are a number of approaches you can take, but here is one solution you can use that involves chaining together the appropriate filters based on the form's posted data:
*Note: To conform to Python's naming convention, rename exReporter class to ExReporter.
# views.py
def process_ex_reporter_form(request):
if request.method == "POST":
# ExReporterForm implementation details not included.
ex_reporter_form = ExReporterForm(request.POST)
if ex_reporter_form.is_valid():
# If form field has no data, cleaned data should be None.
gender = ex_reporter_form.cleaned_data['gender']
age_start = ex_reporter_form.cleaned_data['age_start']
age_end = ex_reporter_form.cleaned_data['age_end']
aggregation_group = ex_reporter_form.cleaned_data['aggregation_group']
aggregation_id = ex_reporter_form.cleaned_data['aggregation_id']
ex_reporters = ExReporter.objects.get_ex_reporters(gender, age_start,
age_end, aggregation_group, aggregation_id)
else:
# Pass back form for correction.
pass
else:
# Pass new form to user.
pass
# models.py
class ExReporterManager(models.Manager):
def get_ex_reporters(self, gender, age_start, age_end, aggregation_group,
aggregation_id):
ex_reporters = super(ExReporterManager, self).get_query_set().all()
# Even though the filters are being applied in separate statements,
# database will only be hit once.
if ex_reporters:
if gender:
ex_reporters = ex_reporters.filter(gender=gender)
if age_start:
ex_reporters = ex_reporters.filter(age__gt=age_start)
if age_end:
ex_reporters = ex_reporters.filter(age__lt=age_end)
# Apply further filter logic for aggregation types supported.
return ex_reporters

Django difficulty saving multiple model objects within save method

This is a hard question for me to describe, but I will do my best here.
I have a model that is for a calendar event:
class Event(models.Model):
account = models.ForeignKey(Account, related_name="event_account")
location = models.ForeignKey(Location, related_name="event_location")
patient = models.ManyToManyField(Patient)
datetime_start = models.DateTimeField()
datetime_end = models.DateTimeField()
last_update = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True)
event_series = models.ForeignKey(EventSeries, related_name="event_series", null=True, blank=True)
is_original_event = models.BooleanField(default=True)
When this is saved I am overriding the save() method to check and see if the event_series (recurring events) is set. If it is, then I need to iteratively create another event object for each recurring date.
The following seems to work, though it may not be the best approach:
def save(self, *args, **kwargs):
if self.pk is None:
if self.event_series is not None and self.is_original_event is True :
recurrence_rules = EventSeries.objects.get(pk=self.event_series.pk)
rr_freq = DAILY
if recurrence_rules.frequency == "DAILY":
rr_freq = DAILY
elif recurrence_rules.frequency == "WEEKLY":
rr_freq = WEEKLY
elif recurrence_rules.frequency == "MONTHLY":
rr_freq = MONTHLY
elif recurrence_rules.frequency == "YEARLY":
rr_freq = YEARLY
rlist = list(rrule(rr_freq, count=recurrence_rules.recurrences, dtstart=self.datetime_start))
for revent in rlist:
evnt = Event.objects.create(account = self.account, location = self.location, datetime_start = revent, datetime_end = revent, is_original_event = False, event_series = self.event_series)
super(Event, evnt).save(*args, **kwargs)
super(Event, self).save(*args, **kwargs)
However, the real problem I am finding is that using this methodology and saving from the Admin forms, it is creating the recurring events, but if I try to get self.patient which is a M2M field, I keep getting this error:
'Event' instance needs to have a primary key value before a many-to-many relationship can be used
My main question is about this m2m error, but also if you have any feedback on the nested saving for recurring events, that would be great as well.
Thanks much!
If the code trying to access self.patient is in the save method and happens before the instance has been saved then it's clearly the expected behaviour. Remember that Model objects are just a thin (well...) wrapper over a SQL database... Also, even if you first save your new instance then try to access self.patient from the save method you'll still have an empty queryset since the m2m won't have been saved by the admin form yet.
IOW, if you have something to do that depends on m2m being set, you'll have to put it in a distinct method and ensure that method get called when appropriate
About your code snippet:
1/ the recurrence_rules = EventSeries.objects.get(pk=self.event_series.pk) is just redundant, since you alreay have the very same object under the name self.event_series
2/ there's no need to call save on the events you create with Event.objects.create - the ModelManager.create method really create an instance (that is: save it to the database).