I am making a change to a record in a sqlite3 database using Django, and the save() function doesn't seem to work.
I am able to save changes using the graphical admin app however.
Here is the code to the specific model I'm trying to do the save in:
def pickup(self,item):
room_items = Item.objects.filter(roomID = self.room.id)
items = [ri.item_name for ri in room_items]
if item in items:
i = Item.objects.filter(item_name = item)
i[0].roomID = 0
i[0].playerID = self.id
i[0].save()
return f'{self.name} picked up the {item} from {self.room}'
else:
return f"{item} is not in the room. can't pick it up."
the pickup function is in a class called Player. I am updating an Item record. Any solutions?
Here is the entire models file for those who want more context:
from django.db import models
import string,random
class Room(models.Model):
### Field Columns in Room Table ###
room_name = models.CharField(max_length = 64)
description = models.CharField(max_length=500, default=f"No Room description'" )
up = models.CharField(max_length = 64, default="")
down = models.CharField(max_length = 64, default="")
left = models.CharField(max_length = 64, default="")
right = models.CharField(max_length = 64, default="")
def items(self):
items = Item.objects.filter(roomID = self.id)
return [i.item_name for i in items]
def __str__(self):
return self.room_name
class Player(models.Model):
# uuid = models.UUIDField(default=uuid.uuid4, unique=True)
HP = models.IntegerField(default=10)
name = models.CharField(max_length=64, default=f"Room {random.choice(string.ascii_letters)}")#attempting to generate a random room name using ascii_letters from string library and random.choice()
room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True)
# inventory = models.ForeignKey(Inventory)
def inventory(self):
inventory = Item.objects.filter(playerID = self.room.id)
return [i.item_name for i in inventory]
def pickup(self,item):
print(self.room.id)
room_items = Item.objects.filter(roomID = self.room.id)
items = [ri.item_name for ri in room_items]
if item in items:
i = Item.objects.filter(item_name = item)
i[0].roomID = 0
i[0].playerID = self.id
i[0].save()
i[0].persist()
return f'{self.name} picked up the {item} from {self.room}'
else:
return f"{item} is not in the room. can't pick it up."
def drop_item(self,item):
pass
def initialize(self,start):
# start = input(f"{self.name}, you are outside the PyTower. It is a 10 story tower. There is a treasure chest on the top floor. Do you have what it takes to reach the top??? type 'y' to enter Pytower: ")
if start == 'y':
self.room = Room.objects.get(room_name = "Foyer")
print(f"{self.name}, you have now entered the {self.room.room_name}")
return f"{self.name}, you have now entered the {self.room.room_name}"
else:
print(f"{self.name}, when you're ready for Pytower, you may enter!")
return f"{self.name}, when you're ready for Pytower, you may enter!"
print(self.room.description)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
def move(self,way=""):
# print(self.room[way]) #causes error, Room object not subscriptable
# print(way)
# if self.room[way]:
# pass
if way == 'up':
if not self.room.up:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.up)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
elif way == 'down':
if not self.room.down:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.down)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
elif way == 'left':
if not self.room.left:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.left)
print('in room-', self.room, 'up-',self.room.up, 'down-',self.room.down, 'left-',self.room.left, 'right-', self.room.right)
return self.room
elif way == 'right':
if not self.room.right:
print('you cannot go that way. no rooms there...')
return 'you cannot go that way. no rooms there...'
else:
self.room = Room.objects.get(room_name = self.room.right)
print('in room: ', self.room, 'up:',self.room.up, 'down:',self.room.down, 'left:',self.room.left, 'right:', self.room.right)
return self.room
else:
print('you have entered an invalid direction')
return 'you have entered an invalid direction'
def __str__(self):
if not self.room:
return f"{self.name} is outside."
else:
return f"{self.name} in {self.room}"
class Item(models.Model):
item_name = models.CharField(max_length=64)
strength = models.IntegerField(default=5)
item_type = models.CharField(max_length=64,default="weapon")
# playerID = models.ForeignKey(Player, on_delete=models.CASCADE, null=True)
# roomID = models.ForeignKey(Room, on_delete=models.CASCADE, null=True)
playerID = models.IntegerField(blank=True,null=True)
roomID = models.IntegerField(default=1,null=True,blank=True)
def persist(self):
self.save()
def __str__(self):
return self.item_name
To understand why your model isn't saving, you must first understand how querysets are evaluated. Essentially, anytime you iterate over them, or slice them, they will hit the database, however there are caveats to this.
Consider the following abstract example:
def MyModel(models.Model):
column = models.IntegerField()
>>> MyModel.objects.create(column=1)
<MyModel: MyModel object (1)>
>>> queryset = MyModel.objects.all()
Slicing:
>>> queryset[0].column = 2
>>> queryset[0].save()
>>> queryset[0].column
1
In the example above, I took a slice, eg. queryset[0], which hits the database, then immediately took a second slice, to try and save the changes made, which hits the database a second time. Finally, I took a third slice, which hits the database again.
Since the first slice is not the same object as the object I called .save() on, the changes are not reflected in the database. To fix this, simply save a reference to the slice as a variable:
>>> instance = queryset[0]
>>> instance.column = 2
>>> instance.save()
>>> instance.column
2
In this example, I only hit the database twice: once when I call instance = queryset[0], and a second time in instance.save().
Here is the optimized version of your code:
def pickup(self, item_name):
items = Item.objects.filter(item_name=item_name, roomID=self.room.id)
if items:
item = items[0]
item.roomID = 0
item.playerID = self.id
item.save()
return 'message'
return 'no item'
Related
I want to give users ten point each time they fill out one Survey , so i have this code above and now how to add the 10 point to self user after he fill out one
models.py :
class User(AbstractUser):
user_pic = models.ImageField(upload_to='img/',default="",null=True, blank=True)
coins = models.IntegerField(default=10)
def get_image(self):
if self.user_pic and hasattr(self.user_pic, 'url'):
return self.user_pic.url
else:
return '/path/to/default/image'
def give_coins(user, count):
user.coins = F('coins') + count
user.save(update_fields=('coins',))
user.refresh_from_db(fields=('coins',))
class Survey(models.Model):
name = models.CharField(max_length=200)
published_on = models.DateTimeField('Published DateTime')
def __str__(self):
return self.name
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.published_on <= now
was_published_recently.admin_order_field = 'published_on'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
class Participant(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
participation_datetime = models.DateTimeField('Participation DateTime')
def __str__(self):
return "Participant "+str(self.participation_datetime)
class Question(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question_text = models.CharField(max_length=200)
created_on = models.DateTimeField('Creation DateTime')
def __str__(self):
return self.question_text
views.py :
#register.inclusion_tag('survey/survey_details.html', takes_context=True)
def survey_details(context, survey_id):
survey = Survey.objects.get(id=survey_id)
return {'survey': survey}
#require_http_methods(["POST"])
def submit_survey(request):
form_data = request.POST.copy()
form_items = list(form_data.items())
print("form_items", form_items)
form_items.pop(0) # the first element is the csrf token. Therefore omit it.
survey = None
for item in form_items:
# Here in 'choice/3', '3' is '<choice_id>'.
choice_str, choice_id = item
choice_id = int(choice_id.split('/')[1])
choice = Choice.objects.get(id=choice_id)
if survey is None:
survey = choice.question.survey
choice.votes = choice.votes + 1
choice.save()
if survey is not None:
participant = Participant(survey=survey, participation_datetime=timezone.now())
participant.save()
return redirect('/submit_success/')
so what i must to do if i want to add 10 point to user after he complete one survey
If submit_survey is a call that requires authentication the user will be present on the request request.user.
Add the coins by adding request.user.give_coins(count=10) to the submit_query method.
you have 2 way
work with event driven tools(maybe hard but principled)
set give_coin befor participant.save() on submit_survey
anyway I din't notice, coin is on your absUser model but your Participant has nothing to do with it or relations
So I have django model and I want to override save so that it only saves on certain instances. Is there a way to avoid a save from happening if a condition is met? The idea is if certain conditions defined with an if statement aren't met the instance fails to be saved. so for instance if there is not enough waiters we cancel the save, or if there is not enough tables we do the same.
Here's my code:
class Service(models.Model):
id = models.AutoField(primary_key=True)
arrival = models.DateTimeField(auto_now_add=True)
exit = models.DateTimeField(null=True, blank=True)
waiter = models.ForeignKey('Waiter', on_delete=models.CASCADE)
table = models.ForeignKey('Table', on_delete=models.CASCADE)
total_ammount= models.DecimalField(max_digits=15, decimal_places=2)
def save(self, *args, **kwargs):
if self.id == None:
time = datetime.datetime.now()
# check for waiters
waiters = Waiter.objects.select_related().annotate(num_Service=Count('service', filter=Q(service__exit__gt=time))).all()
available_waiters = waiters.filter(num_Service__lt=4)
avalable_waiters_length = len(available_waiters)
# check for tables
tables = Table.objects.select_related().annotate(num_Service=Count('service', filter=Q(service__exit__gt=time))).all()
available_tables = tables.filter(num_Service__lt=1)
avalable_tables_length = len(available_tables)
# return exception if a problem arises
if avalable_tables_length == 0 and avalable_waiters_length == 0:
print("not enough waiters or tables")
if avalable_waiters_length == 0:
print("not enough waiters")
return
if avalable_tables_length == 0:
print("not enough tables")
return
# assign waiter and table
waiter_obj = random.choice(available_waiters)
self.waiter = waiter_obj
table_obj = random.choice(available_tables)
self.table = table_obj
print(time.time())
# check if current time is open
if datetime.time(9,0) < time.time() and time.time()> datetime.time(21, 30):
print("The restaurant is closed")
return
print(time.time())
# add timedelta to init_time
if time.time() < datetime.time(17,0):
print(time + datetime.timedelta(minutes=90))
self.exit = time + datetime.timedelta(minutes=90)
if time.time() > datetime.time(17,0):
self.exit = time + datetime.timedelta(minutes=120)
#finalize pre_save
return super(Service, self).save(*args, **kwargs)
Thank you in advance :)
you should raise an error, like ValidationError for example
After much research and trouble i came up with a non DRY solution, Hope someone can make it DRY.
All im trying to get is a calculated Price which takes a parameter and displays in the template accordingly.
i have a function get_price on model vehiclecategory which takes a parameter duration which is received from frontend forms.
MODELS.PY
class VehicleCategory(models.Model):
CATEGORY_CHOICES=(
('E-Cycle', 'E-Cycle'),
('E-Scooter', 'E-Scooter')
)
main_category = models.CharField(max_length=15, choices= CATEGORY_CHOICES)
title = models.CharField(unique=True, max_length=200)
image = models.ImageField(
null=True,
blank=True,
width_field="width_field",
height_field= "height_field",
default= 'e-bike.png',
upload_to='category')
width_field = models.IntegerField(default=250)
height_field = models.IntegerField(default=250)
slug =models.SlugField(max_length=200, db_index=True, unique=True)
def __str__(self):
return self.title
#GET PRICE
def get_price(self, duration):
for item in VehiclePrice.objects.all():
if item.vehicle_category.title == self.title and (duration >= item.slab.start and duration <= item.slab.end):
return item.total_price
class Meta():
verbose_name = "Vehicle Category"
verbose_name_plural = "Vehicle Categories"
class PriceSlab(models.Model):
start = models.IntegerField()
end = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s ' % (self.start, self.end)
class VehiclePrice(CustomerStatus):
help_text= "Ensure no more than 2 digits after decimal"
vehicle_category = models.ForeignKey(VehicleCategory, on_delete= models.SET_NULL, null=True, related_name='vehicle_category_price')
slab = models.ForeignKey(PriceSlab, on_delete=models.CASCADE)
net_price = models.DecimalField(help_text= help_text, max_digits=5, decimal_places=2)
tax_percent = models.DecimalField(help_text=help_text, max_digits=4, decimal_places=2, default=18.00)
discount_percent = models.DecimalField(help_text=help_text,max_digits=4, decimal_places=2, default=0, blank=True)
#property
def total_tax(self):
tax = (self.net_price * self.tax_percent)/100
return tax
#property
def get_price(self):
total = self.net_price + self.total_tax
return total
#property
def total_discount(self):
discount = (self.get_price * self.discount_percent)/100
return discount
#property
def total_price(self):
total = self.get_price - self.total_discount
return round(total)
class Meta():
unique_together=('customer_status','vehicle_category' ,'slab')
def __str__(self):
return '%s - %s - %s' % (self.customer_status, self.vehicle_category, self.slab)
VIEWS.PY
class HomeView(ListView):
template_name = 'app/home.html'
def get(self, request):
if request.method == "GET":
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
if start_date and end_date:
start_date = datetime.strptime(start_date, "%d/%m/%Y").date()
end_date = datetime.strptime(end_date, "%d/%m/%Y").date()
duration = (end_date - start_date).days +1
print(duration)
vehiclecategory= VehicleCategory.objects.all()
context = {
'price1': VehicleCategory.objects.get(main_category= 'E-Cycle', title="Sporty").get_price(duration),
'price2': VehicleCategory.objects.get(main_category= 'E-Cycle', title="Step-Through").get_price(duration),
'price3': VehicleCategory.objects.get(main_category= 'E-Cycle', title="Fatbike").get_price(duration),
'price4': VehicleCategory.objects.get(main_category= 'E-Scooter', title="Scooter").get_price(duration),
'vehiclecategory1': vehiclecategory.filter(main_category= 'E-Cycle', title="Sporty"),
'vehiclecategory1': vehiclecategory.filter(main_category= 'E-Cycle', title="Step-Through"),
'vehiclecategory1': vehiclecategory.filter(main_category= 'E-Cycle', title="Fatbike"),
'vehiclecategory2': vehiclecategory.filter(main_category= 'E-Scooter', title="Scooter"),
'form':CartQuantityForm(),
'dateform': DateForm(),
}
else:
context={'dateform': DateForm(),}
return render(request, self.template_name, context )
after the user inputs the date range, the vehicles are displayed, but when u go to the cart and come back the same page, the page refreshes as a new one. how can keep the date range values intact and render the same page as the user got first time he searched for a vehicle, so that he can add or modify the vehicles selected???
You may put your start & end dates into your URL.
You can create 2 urls record dispatching the same view:
path(r'/prices/', HomeView.as_view())
path(r'/prices/(?P<start>\d{4}-\d{2}-\d{2})_(?P<end>\d{4}-\d{2}-\d{2})', HomeView.as_view())
Then you need to make some changes in your view:
class HomeView(ListView):
template_name = 'app/home.html'
def get(self, request, **kwargs):
start = kwargs.get('start')
end = kwargs.get('end')
if start is None or end is None:
# Ask for dates & Redirect to its new url with dates.
else:
# Check the dates, convert them to date object & do the rest.
Maybe not the best solution but the first thing came to my mind is this one.
I need to create the new object or just update if already existing. I receive: QuerySet' object has no attribute "seat". Don't know what I'm doing wrong.
models:
class rows_and_seats(models.Model):
movie = models.ForeignKey(Movies, on_delete=models.CASCADE)
row = models.CharField(max_length = 1)
number = models.IntegerField()
def __str__(self):
return f'{self.movie}'
class Reservation(models.Model):
customer = models.ForeignKey(User, on_delete=models.CASCADE)
movie = models.ForeignKey(Movies, on_delete=models.CASCADE)
seat = models.ManyToManyField(rows_and_seats)
ordered = models.DateTimeField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), blank=True, null=True)
def __str__(self):
return f'{self.customer.username}:{self.movie.title}:{self.ordered}'
views
#login_required
def buy_seats(request, pk):
if request.method == "POST" and request.session.get("seats"):
seats = request.session.pop("seats")
movie = Movies.objects.get(pk=pk)
customer = User.objects.get(pk=request.user.id)
for s in seats:
user_reserved_seats = rows_and_seats.objects.get(movie=movie, row=s[:1], number=int(s[2:]))
reservation_check = Reservation.objects.filter(customer=customer, movie=movie)
if reservation_check.exists():
reservation_check.seat.add(user_reserved_seats)
else:
new_reservation = Reservation.objects.create(customer=customer, movie=movie)
new_reservation.seat.add(user_reserved_seats)
messages.success(request,"You have succesfully reserved the seats.")
return redirect("home")
return redirect("home")
My goal is to keep rows_and_seat in manyTomany in order to display only one reservation of user in admin panel, instead of the list of repeating itself titles.
You can access the value after the exists() check:
if reservation_check.exists():
reservation_check.first().seat.add(user_reserved_seats)
else:
new_reservation = Reservation.objects.create(customer=customer, movie=movie)
new_reservation.seat.add(user_reserved_seats)
Maybe you can use something like get_or_create:
user_reserved_seats = rows_and_seats.objects.get(movie=movie, row=s[:1], number=int(s[2:]))
reservation, created = Reservation.objects.get_or_create(
customer=customer, movie=movie,
)
reservation.seat.add(user_reserved_seats)
Also you might be looping over the seats too many times, maybe you can add all the seats in only one assignment.
if "allotted_pto" (paid time off) is an integer field (expressing number of days) in a UserProfile model:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
fullname = models.CharField(max_length=64, unique=False)
company = models.CharField(max_length=50, choices=CLIENT_CHOICES)
...
allotted_pto = models.IntegerField(max_length=2, blank=True, null=True)
...
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
and "total_days" returns an integer from a vacation request model:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
...
total_days = models.IntegerField(max_length=2, blank=True, null=True)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
fromdate = self.start_date
todate = self.return_date
daygenerator = (fromdate + timedelta(x + 1) for x in xrange((todate - fromdate).days))
self.total_days = sum(1 for day in daygenerator if day.weekday() < 5)
super(LeaveRequest, self).save(*args, **kwargs)
...
how can I construct a view that gives me the sum of "total_days" from a filter set of records and subtract that sum from the "allotted_pto" in the user profile? The simple view I wrote (see below) produces the number of "total_days" objects (in dictionary form) as opposed to counting the actual days, and the request for "allotted_pto" is apparently incorrectly constructed because it returns nothing at all...
#views.py
def leave_screen(request, id):
profile = UserProfile.objects.get(user=id)
records = LeaveRequest.objects.filter(employee=id)
agg_pto = LeaveRequest.objects.aggregate(Count('total_days'))
if profile.allotted_pto: #if the allotted_pto field in UserProfile is not empty
allotted_pto = profile.allotted_pto
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
return render_to_response("vacation/leave_request.html", {'records': records, 'agg_pto': agg_pto, 'remaining_pto': remaining_pto})
ok, figured out calculation:
def leave_screen(request, id):
...
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
just have to figure out how to pull the allotted_pto integer from the User Profile model.
ok, so this wasn't as difficult as I thought. The first challenge was to get an aggregate sum of objects. My first attempt was close but I should have just used "Sum" as opposed to "Count":
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
then I just used the python method for extracting the value from a dictionary:
agg_pto = agg_pto['total_days__sum']
finally:
def leave_screen(request, id):
user = request.user.id
profile = request.user.get_profile()
records = LeaveRequest.objects.filter(employee=id).order_by('-submit_date')
agg_pto = LeaveRequest.objects.filter(employee=id).aggregate(Sum('total_days'))
agg_pto = agg_pto['total_days__sum']
allotted_pto = profile.allotted_pto
if allotted_pto: #if the allotted_pto field in UserProfile is not empty
remaining_pto = allotted_pto - agg_pto
else:
remaining_pto = "na"
supervised_records = LeaveRequest.objects.filter(supervisor=id).order_by('-submit_date')
return render_to_response("vacation/leave_request.html", {'records': records, 'supervised_records': supervised_records, 'agg_pto': agg_pto, 'allotted_pto': allotted_pto, 'remaining_pto': remaining_pto, 'profile': profile })
I don't know why it was so hard for me to figure out the syntax for pulling objects from the UserProfile. But I do know that the django-debug-toolbar is very helpful.