How to update a field in the model using ForegnKey lookup? - django

I need to be able to update a field num_places in the Event model accessing it via event ForeignKey in the Participant model when the last is saved.
Here are my models.py:
class Event(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500)
text = models.TextField()
image = models.ImageField(blank=True)
date = models.DateTimeField()
price = models.PositiveIntegerField()
num_places = models.PositiveIntegerField(default=50)
slug = models.SlugField()
class Participant(models.Model):
name = models.CharField(max_length=200)
participant_uuid = models.UUIDField(primary_key=False, verbose_name='UUID')
email = models.EmailField()
phone_regex = RegexValidator(regex=r'^\+7\d{10}$')
phone_number = models.CharField(validators=[phone_regex], max_length=12)
num_places = models.PositiveIntegerField(default=1)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
paid = models.BooleanField(default=False)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
try:
self.full_clean(exclude=None)
self.event.num_places -= self.num_places # the value isn't updated
super().save(*args, **kwargs)
self.valid = True
self.non_field_errors = False
except ValidationError as e:
self.non_field_errors = e.message_dict[NON_FIELD_ERRORS]
self.valid = False
class Meta:
unique_together = ('name', 'email', 'phone_number', 'event')
The code with a comment has a problem: the num_places value in the Event model stays unchanged. How to fix it?

you need to save the modified object of Event.
self.event.save()
def save(self, *args, **kwargs):
try:
self.full_clean(exclude=None)
self.event.num_places -= self.num_places # the value isn't updated
self.event.save()
"""watch out this since you wanna do it at the end after setting valid, and non_fieild_errors"""
super().save(*args, **kwargs)
self.valid = True
self.non_field_errors = False
except ValidationError as e:
self.non_field_errors = e.message_dict[NON_FIELD_ERRORS]
self.valid = False

Related

How to filter in Django Rest Framework function based view?

So many documentation for filtering in Django rest framework but all the examples are in class based view. but I am trying to do the same in DRF function based view. I wanted to do multiple filter for my items queryset.
I tried one way and it is working perfectly. Here first I am trying to search by item name or restaurant name in one request. then I take another keyword and try to filter restaurant name or item name based on restaurant city. It is working perfectly like if I hit this url
http://localhost:8000/api/items/?keyword=lasagne&keyword1=paris
then it gives me the perfect response.
But What I am asking for is that now my code looks for this specific part is messy and I want to add more fields for multiple filtering. Which procedure to follow? Should I follow this one and multiple requests and trying to filter from this.
Suppose now I want to filter the queryset based on dish_type, price, item_type, location and then search for items by name or restaurants by name
#this is my models
class Restaurant(models.Model):
user = models.OneToOneField(CustomUser, related_name='restaurant', on_delete=models.CASCADE, null=False)
name = models.CharField(max_length=200, blank=True, null=True)
profile_picture = models.ImageField(null=True, blank=True)
address = models.TextField(max_length=2000, blank=True, null=True)
city = models.CharField(max_length=200)
latitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
longitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
is_verified = models.BooleanField(default=False)
createdAt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.name)
class Item(models.Model):
user = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
name = models.CharField(max_length=220)
image = models.ImageField(null=True, blank=True)
dish_type = models.ForeignKey(Dishtype, on_delete=models.CASCADE)
item_type = models.ForeignKey(Itemtype, on_delete=models.CASCADE)
description = models.TextField(max_length=10000)
rating = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
old_price = models.DecimalField(max_digits=11, decimal_places=2)
discount = models.IntegerField(blank=True, null=True)
price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
countInStock = models.IntegerField(blank=True, null=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def save(self, *args, **kwargs):
self.price = Decimal(self.old_price * (100 - self.discount) / 100)
return super(Item, self).save(*args, **kwargs)
class Meta:
ordering = ['-createdAt']
def __str__(self):
return self.name
#serializer
class RestaurantSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Restaurant
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = UserSerializer(user, many=False)
return serializer.data
class ItemSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
dish_type = serializers.SerializerMethodField(read_only=True)
item_type = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Item
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = RestaurantSerializer(user, many=False)
return serializer.data
def get_dish_type(self, obj):
dish_type = obj.dish_type
serializer = DishtypeSerializer(dish_type, many=False)
return serializer.data
def get_item_type(self, obj):
item_type = obj.item_type
serializer = ItemtypeSerializer(item_type, many=False)
return serializer.data
#views.py
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
query = request.query_params.get('keyword')
if query == None:
query = ''
cuery = request.query_params.get('keyword1')
if cuery == None:
cuery = ''
items = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all().filter(
Q(name__icontains = query) | Q(user__name__icontains = query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all(
).filter(Q(user__city__iexact = cuery))
items = restaurant_city.filter(Q(name__icontains = query) | Q(user__name__icontains = query))
serializer = ItemSerializer(items, many=True)
return Response(serializer.data)
######Updated solved the problem
#filters.py
class ItemFilter(django_filters.FilterSet):
numReviews = django_filters.NumberFilter()
numReviews__gt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='gt')
numReviews__lt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='lt')
name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Item
fields = ['_id', 'dish_type__id']
#views
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
queryset = Item.objects.all()
filterset = ItemFilter(request.GET, queryset=queryset)
if filterset.is_valid():
queryset = filterset.qs
serializer = ItemSerializer(queryset, many=True)
return Response(serializer.data)
now data are passing like this
http://localhost:8000/api/items/?numReviews__gt=20&numReviews__lt=22
You can use queryset and override get_queryset function.
class FooViewSet(GenericViewSet, mixins.ListModelMixin):
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = ItemSerializer
def get_queryset(self):
query = self.request.query_params.get('keyword', '')
if not self.request.query_params.get('keyword1'):
items = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all().filter(
Q(name__icontains=query) | Q(user__name__icontains=query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all(
).filter(Q(user__city__iexact=self.request.query_params.get('keyword1', '')))
items = restaurant_city.filter(Q(name__icontains=query) | Q(user__name__icontains=query))
return items

Django: Delete or empty content of a row in UpdateView

I'm looking to delete or empty a specific row in my table/model in my UpdateView. I have a team and employees in the team. I have made an update view that when "yes" is pressed, the team becomes archived. I want to additionally delete or empty the employee's numbers when doing so. How would I approach that?
I know it might be weird, but the idea is that the employee's numbers should be destroyed once the team is archived, while the rest of the data still stands.
Team Model
class Team(models.Model):
slug = models.SlugField(max_length=200)
teamname = models.CharField(max_length=50, help_text="Indtast holdnavn.", null=False, primary_key=True)
is_active = models.BooleanField(default=True)
Employee Model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
emp_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
teamname = models.ForeignKey('Hold', on_delete=models.CASCADE, null=True)
UpdateView
My updateView is using team, as its that model I'm updating.
class ArchiveHoldView(UpdateView):
template_name = 'evalsys/medarbejder/archive_hold.html'
model = Team
form_class = ArchiveForm
def archive_view_team_with_pk(self, slug=None):
if slug:
team = Team.objects.get(slug=slug)
else:
team = self.team
args = {'team': team}
return render(self, 'evalsys/medarbejder/archive_hold.html', args)
def get_context_data(self, **kwargs):
context = super(ArchiveHoldView, self).get_context_data(**kwargs)
context['is_active'] = Team.objects.get(slug=self.kwargs.get('slug'))
return context
def get_success_url(self):
return reverse_lazy("evalsys:home")
Update View form
class ArchiveForm(forms.ModelForm):
def save(self, *args, **kwargs):
self.instance.is_active = False
return super(ArchiveForm, self).save(*args, **kwargs)
is_active = BooleanField(required=False, widget=forms.HiddenInput())
class Meta:
model = Team
fields = ['is_active', ]
labels = {'is_active': 'Is Active'}

How to update data in django model to title case in views?

I'm trying to change a column of strings like 'HOSPITAL ZERO', "HOSPITAL ONE" from the database into title case or 'Hospital zero' in the views.py or models.py. I've tried both and can't get either to work for me.
Here is my code in views.py. Column is in Hospital Model under name, i.e. Hospital.name.
def results(request):
if request.method == "GET":
Q=()
out_words = request.GET.get('s')
context = RequestContext(request)
#here is where I tried to change it
qs = Price.objects.annotate(hospital__name=Lower('hospital__name'))
table = PriceTable(qs.filter(service__desc_us__icontains = out_words))
RequestConfig(request, paginate={'per_page': 10}).configure(table)
RequestConfig(request).configure(table)
else:
table = PriceTable(Price.objects.all())
RequestConfig(request).configure(table)
return render(request, 'results.html', {'table': table})
Here is how I tried in model.py.
class Hospital(models.Model):
"""Model representing Hospitals."""
hid = models.CharField(max_length = 8, null=True)
name = models.CharField(max_length=200, primary_key=True)
hopid = models.UUIDField(default=uuid.uuid4, help_text='Unique ID for this particular hospital in database')
address = models.CharField(max_length = 200, null = True)
class Meta:
ordering = ['hopid']
#here is where i tried to update it
def save(self, *args, **kwargs):
self.name = self.name.title()
return super(Hospital, self).save(*args, **kwargs)
def __str__(self):
"""String for representing the Model object."""
return f'{self.name} ({self.address})'
class Price(models.Model):
"""Model with all the hospital prices by service."""
priceid = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular service in database')
comments = models.CharField(max_length = 200, blank = True, null =True)
hospital = models.ForeignKey("Hospital", on_delete=models.SET_NULL, null=True)
class Meta:
ordering =['priceid']
def __str__(self):
return f'{self.hospital.name} (...)'
You can try this:
"HOSPITAL ONE".lower().capitalize()
my_string.lower().capitalize()
Here is an option:
def save(self, *args, **kwargs):
self.name = self.name.lower().capitalize()
return super(Hospital, self).save(*args, **kwargs)
Here's a new method for your Hospital model called get_title_case_name. Now you can access a hospital name by calling hospital_instance.get_title_case_name().
class Hospital(models.Model):
"""Model representing Hospitals."""
hid = models.CharField(max_length = 8, null=True)
name = models.CharField(max_length=200, primary_key=True)
hopid = models.UUIDField(default=uuid.uuid4, help_text='Unique ID for this particular hospital in database')
address = models.CharField(max_length = 200, null = True)
class Meta:
ordering = ['hopid']
def __str__(self):
"""String for representing the Model object."""
return f'{self.name} ({self.address})'
def get_title_case_name(self):
return self.name.capitalize()
Just add method to model:
def get_title_case_name(self):
return self.name.title()
In views/template you can run this method against model object.

Select a valid choice ModelMultipleChoiceField error

I'm trying to make form that allows to user selecting employers by the checkbox list, depends on their team using ModelMultipleChoiseField. I can filter proper employers per team but when i select any of then i had error
Select a valid choice. 3 is not one of the available choices.
And i don't know what is wrong ;/
This is my form class :
class TaskDictForm(forms.Form):
team_id = 0
order = forms.IntegerField(min_value=0)
name = forms.CharField(max_length=200)
start = forms.DateTimeField()
previous = forms.CharField(max_length=200)
employers = forms.ModelMultipleChoiceField(queryset=None, widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
if hasattr(self, 'initial_values') and not kwargs.get('data'):
for field_name, value in self.initial_values.items():
if not getattr(kwargs.get('instance', None), field_name, None):
initial[field_name] = value
kwargs.update({'initial': initial})
if hasattr(self, 'required_fields'):
for field_name in self.required_fields:
self.fields[field_name].required = True
tmp = kwargs.pop('pk', None)
self.team_id = tmp
super(TaskDictForm, self).__init__(*args, **kwargs)
self.fields['employers'].queryset = Employer.objects.filter(team=self.team_id)
Employer and Team models:
class Team(models.Model):
name = models.CharField(default='Team', max_length=100, unique=True)
def __str__(self):
return self.name
class Employer(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True)
team_leader = models.BooleanField(default=False)
project_menager = models.BooleanField(default=False)
# project_id = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
hourly_rate = models.IntegerField(default=0)
working_time = models.FloatField(default=0.0)
def __str__(self):
return (self.user.first_name + ' ' + self.user.last_name)
The same proble is when i try use MultipleChoiseField

Django models Foreign key autopopulate

Am having two model classes user and accounts linked by together or connected by another table called useraccounts.
What i want is for the useraccounts table to be automatically also be populated with the ids of the user and account table when i submit data to them(user and accounts).
here is my sample models code.
class Account(models.Model):
# fields
id = models.IntegerField(primary_key=True)
t_stamp = models.DateField(default=datetime.datetime.now())
acctno = models.TextField(null=False, unique =True)
acctname = models.TextField(null=False)
status = models.TextField(null=False, choices=STATUS)
accttype = models.TextField(null=False,choices=ACCT_TYPE)
acctclass = models.TextField(null=False, choices=ACCT_CLASS)
min_balance = models.FloatField(default=0)
cur_balance = models.FloatField(default=0)
ava_balance = models.FloatField(default=0)
#fundingsources = models.ManyToManyField(FundingSource)
class Meta:
managed = False
db_table = 'accounts'
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
username = models.TextField(unique=True, null=True)
fullname = models.TextField(null=False)
country = models.TextField(null=True)
email = models.EmailField( unique=True, db_index=True)
phone = models.TextField()
address = models.TextField()
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField(null=True)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=True)
#accounts = models.ManyToManyField(Account, through='UserAccount')
class Meta:
db_table = "users"
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def get_short_name(self):
return self.fullname
def get_username(self):
return self.email
def is_authenticated(self):
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
signals.post_save.connect(create_auth_client, sender=User)
User._meta.get_field_by_name('email')[0]._unique=True
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'
You have to do that in model save method:
class Account(models.Model):
id = models.IntegerField(primary_key=True)
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
def save(self, *args, **kwargs):
super(User, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(user_id=self,account_id=account) for account in Account.objects.all()])