There is an application model. The meaning of it is quite simple, the Author submits the application, the site administrator appoints the Contractor for its execution, which after completing the received application completes it. Everything seems to be fine, the application model was created, made sure that after the administrator chooses the Contractor the application immediately received the status of closed.
But here is one problem that I can not cope with when the Administrator closes the application (Status "Completed"), then the application does not acquire the status Completed, because the Artist is assigned to it because of the save function in the model.
How to make the status of the application complete, even if a contractor is appointed to this application? In advance I ask you to excuse me for such a presentation of the question, I'm still a beginner. Many thanks to all those who can help)
models.py
class Application(models.Model):
STATUS_CHOICES = (
('In_the_work', 'В работе'),
('New', 'Новая'),
('Complited', 'Завершена')
)
author = models.ForeignKey('auth.User', related_name = '+', verbose_name = 'Автор')
title = models.CharField(max_length=50, verbose_name = 'Заголовок')
text = models.TextField(verbose_name = 'Описание проблемы')
room = models.CharField(max_length = 4, verbose_name = 'Кабинет')
published_date = models.DateField(blank=True, null=True, default =
datetime.datetime.now, verbose_name = 'Дата')
status = models.CharField(max_length=15, choices=STATUS_CHOICES,
default='Новая', verbose_name = 'Статус')
owner = models.ForeignKey('auth.User', related_name = '+', null = True,
blank = True, limit_choices_to={ 'groups__name': 'Техническая поддержка'},
verbose_name = 'Исполнитель')
def save(self, *args, **kwargs):
if self.owner != None:
self.status = 'In_the_work'
super(Application, self).save(*args, **kwargs)
I am not sure about what you mean about an "Artist" being assigned to the application.
When you mean that a contractor is assigned to it, I am thinking you are referring to assigning an "owner" (owner field value) to the Application model. If that is the case, the error is that, in your save method you are checking for the owner being different than None, in which case you always overwrite the status to "In_the_work".
So maybe a solution would be something like:
def save(self, *args, **kwargs):
if self.status != 'Complited' and self.owner is not None:
self.status = 'In_the_work'
super(Application, self).save(*args, **kwargs)
In that way, you are only overwritting status if status is different than complited in the first place, not everytime the owner is not None.
Thank you.
Related
I am working on a school project using Django and Python.
Now I have created a website for using rest-apis. However, while testing I encountered an error that I simply can't seem to be able to solve. It occurs whenever I try to create a movie with POSTMAN using the api.I get the following error over and over again and I can't find what is wrong.
Error message:
raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg))
TypeError: Movie() got an unexpected keyword argument 'actors'
My serializer
class MovieSerializer(serializers.ModelSerializer):
# Queryset gets all the data from the Actor model as specified with objects.all()
actor_pks = serializers.PrimaryKeyRelatedField(queryset=Actor.objects.all(), source='actors', write_only=True,
label='Actors', many=True)
rent_pks = serializers.PrimaryKeyRelatedField(source='rent', read_only=True, label='Rent')
# Change image-options to allow post/put/patch without an image
image = serializers.ImageField(allow_null=True, required=False)
def __init__(self, *args, **kwargs):
# Get additional parameters from constructor
depth = kwargs.pop('depth', None)
fields = kwargs.pop('fields', None)
# Add diffrent pks to fields if field is not None from constructor
fields.append('actor_pks') if fields is not None else None
fields.append('rent_pks') if fields is not None else None
fields.append('image') if fields is not None else None
# Overwrite meta tags
self.Meta.depth = depth if depth is not None else 1
self.Meta.fields = fields if fields is not None else '__all__'
# Call super-constructor
super(MovieSerializer, self).__init__(*args, **kwargs)
class Meta:
model = Movie
My Movie Model:
class Movie(models.Model):
"""
Movie-Model. Many-to-Many relation with Rent.
"""
title = models.CharField(null=False, max_length=50)
publishdate = models.CharField(null=False, max_length=20)
genre = models.CharField(null=True, max_length=50)
is_borrowed = models.BooleanField(null=False, default=False)
image = models.ImageField(null=False, upload_to='movies', default='noimage.png')
rent = models.ManyToManyField(Rent, blank=False)
actor = models.ForeignKey(Actor, null=False, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return '{}'.format(self.title)
def save(self, *args, **kwargs):
if not self.image:
if self.id:
self.image = Movie.objects.get(pk=self.id).image
else:
self.image = 'noimage.png'
super().save(*args, **kwargs)
I would be glad for any help with this problem. I have been working on it quite a while now and I am simply to dumb to find a solution for this.
Thanks for your help lads.
After a lot of time I was able to find the the answer to the question.
In the models in my Movie model, I had the following field implemented.
actor = models.ForeignKey(Actor, null=False, on_delete=models.CASCADE)
Turns out I had it's relation set wrong and therefor the serializer threw the error over and over again, since he was not able to make the connection, even if I changed the model field to "actors".
The solution was to turn the actor = models.ForeignKey to the following:
actor = models.ManyToManyField(Actor, related_name='movie_list', blank=False)
As well as change the actor_pks variable to the following:
actor_pks = serializers.PrimaryKeyRelatedField(queryset=Actor.objects.all(), source='actor', write_only=True, label='Actors', many=True)
I have created a knowledge structure that has "blocks"and each block has children to cater for different situations.
The code is:
models.py
class KBSBlock(models.Model):
name = models.CharField(max_length=150, unique=True)
code = models.CharField(max_length=4, blank=True)
status=models.CharField(max_length=1, choices=Status_Choices, default='Draft')
enter_by = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT)
tags = TaggableManager(blank=True)
attribute1 = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if self.code is None or self.code == "":
self.code = create_code4(self)
super(KBSBlock, self).save(*args, **kwargs)
#receiver(post_save, sender=KBSBlock)
def create_block(sender, instance, created, **kwargs):
if created:
#create_block = BlockDetails.objects.create(block_dts=instance)
print('Working!')
class BlockDetails(models.Model):
block_dts = models.ForeignKey('KBSBlock', on_delete=models.CASCADE)
code = models.CharField(max_length=2, blank=True)
attribute1 = models.CharField(max_length=100, default='All')
created_at = models.DateTimeField(auto_now_add=True)
enter_by = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.PROTECT)
status=models.CharField(max_length=1, choices=Status_Choices, default='Draft')
Whenever I create a block, I want to create a generic detail in BlockDetails for the block with (code='00', attribute1='All', enter_by='request.user')
It prints the 'working' bit with the 'create_block' line hashed out.
I am using PostgreSQL, Django 2.1 and Python 3.7, and cannot get it right.
Help Please
First of all thanks to #Dani Herrera and #Davit Tovmasyan! Between the two of them I figured out what the problem was: Turns out I had a few things wrong.
The error was coming from the database: value too long for type character varying(1) telling me that I was trying to enter a string that was too long for the intended field. This field was the status field - It appears that even though the choices option worked perfectly in normal circumstances, the signal command wanted the short form of the choice only.
The correct code is as follows:
#receiver(post_save, sender=KBSBlock)
def create_block(sender, instance, created, **kwargs):
if created:
instance.blockdetails_set.create(block_dts=instance.name, code='00', enter_by=instance.enter_by, attribute1='All', status='D')
NB: the case of the modelname MUST be lowercase, even in the model class has uppercase letters
Once I corrected that - everything worked.
I have a quite peculiar issue, regarding creating a formset out of a certain form. The thing is, that the form has to has an undisclosed number of fields, so I want to pass a variable to that form, which will take a proper model from database, and create proper fields within form's init method.
StrangeForm(forms.Form):
def __init__(self, id, *args, **kwargs):
super(StrangeForm, self).__init__(*args, **kwargs)
# get the form's base entry
self.certain_entry = Certain_Entry.objects.get(id=id)
# create a hidden input to keep the id
self.fields["id"] = forms.HiddenInput()
self.fields["id"].initial = id
# initiate text field
self.fields["text_field"] = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:95%', }),
required=False)
self.fields["text_field"].initial = self.certain_entry.text
# create and initiate fields for undisclosed number of subentries
subordinates = Certain_Entry_Subordinates.objects.filter(foreign_key=self.certain_entry)
for each in subordinates:
the_name = "%d_%s_%s" % (each.further.id, each.further.name)
self.fields[the_name] = forms.ChoiceField(choices=(("Ok", "Ok"), ("Not Ok", "Not Ok"), ("Maybe" "Maybe"),
required=False)
self.fields[the_name].initial = each.option
The models are as follows:
class Certain_Entry(models.Model):
text = models.TextField(max_length=128, null=True, blank=True)
class Certain_Entry_Subordinates(models.Model):
certain_entry = models.ForeignKey(Certain_Entry, null=False, blank=False)
option = models.TextField(max_length=8, null=True, blank=True)
further = models.ForeignKey(Further, null=False, blank=False)
class Further(models.Model):
name = models.TextField(max_length=32, null=True, blank=True)
So as you see, I need to pass this ID into the form. When it's a single form, it's all OK, but I can't find any information as to how create a formset with forms that require a variable. Any ideas?
PS: As to WHY... Don't ask, I just need to do it that way, trust me. I'm an engineer.
I am working on a library system to manage certain items in our office, I don't need a full-blown integrated library system so I decided to hand roll one with Django.
Below is a simplified version of my model:
class ItemObjects(models.Model):
# Static Variables
IN_STATUS = 'Available'
OUT_STATUS = 'Checked out'
MISSING = 'Missing'
STATUS_CHOICES = (
(IN_STATUS, 'Available'),
(OUT_STATUS, 'Checked out'),
(MISSING, 'Missing'),
)
# Fields
slug = models.SlugField(unique=True)
date_added = models.DateField(auto_now_add=True)
last_checkin = models.DateTimeField(editable=False, null=True)
last_checkout = models.DateTimeField(editable=False, null=True)
last_activity = models.DateTimeField(editable=False, null=True)
status = models.CharField(choices=STATUS_CHOICES, default=IN_STATUS, max_length=25)
who_has = models.OneToOneField(User, blank=True, null=True)
times_out = models.PositiveIntegerField(default=0, editable=False)
notes = models.CharField(blank=True, max_length=500)
history = models.TextField(blank=True, editable=False)
pending_checkin = models.BooleanField(default=False)
pending_transfer = models.BooleanField(default=False)
At first I was using a method on ItemObject to process checking out an item to a user and who_has was an EmailField because I couldn't get a CharfField to populate with the logged in user's name, but I figured using a OneToOneField is probably closer to the "right" way to do this.. While who_has was an EmailField, the following method worked:
def check_out_itemobject(self, user):
user_profile = user.get_profile()
if self.status == 'Available' and self.who_has == '':
self.status = 'Checked out'
self.who_has = user.email
self.last_checkout = datetime.datetime.now()
self.last_activity = datetime.datetime.now()
self.times_out += 1
if self.history == '':
self.history += "%s" % user_profile.full_name
else:
self.history += ", %s" % user_profile.full_name
if user_profile.history == '':
user_profile.history += self.title
else:
user_profile.history += ", %s" % self.title
else:
return False # Not sure is this is "right"
user_profile.save()
super(ItemObjects, self).save()
Now that I am using a OneToOneField this doesn't work, so I started looking at using a subclass of ModelForm but none of the cases I saw here on SO seemed to apply for what I am trying to do; my form would be a button, and that's it. Here are some of the questions I looked at:
Django: saving multiple modelforms simultaneously (complex case)
(Django) (Foreign Key Issues) model.person_id May not be NULL
django update modelform
So was I on the right track with a sort of altered save() method, or would a ModelForm subclass be the way to go?
EDIT/UPDATE: Many thanks to #ChrisPratt!
So I am trying to get Chris Pratt's suggestion for showing ItemHistory to work, but when I try to render it on a page I get an AttributeError that states "'User' object has no attribute 'timestamp'". So my question is, why is it complaining about a User object when last_activity is an attribute on the ItemObject object ?
My view:
#login_required
def item_detail(request, slug):
item = get_object_or_404(Item, slug=slug)
i_history = item.last_activity
user = request.user
return render_to_response('items/item_detail.html',
{ 'item' : item,
'i_history': i_history,
'user' : user })
I do not see why a User object is coming up at this point.
EDIT2: Nevermind, history is clearly a M2M field whose target is User. That's why!
Assuming users will log in and check out books to themselves, then what you most likely want is a ForeignKey to User. A book will only have one User at any given time, but presumably Users could check out other items as well. If there is some limit, even if the limit is actually one per user, it would be better to validate this in the model's clean method. Something like:
def clean(self):
if self.who_has and self.who_has.itemobject_set.count() >= LIMIT:
raise ValidationError('You have already checked out your maximum amount of items.')
Now, you checkout method has a number of issues. First, status should be a defined set of choices, not just random strings.
class ItemObject(models.Model):
AVAILABLE = 1
CHECKED_OUT = 2
STATUS_CHOICES = (
(AVAILABLE, 'Available'),
(CHECKED_OUT, 'Checked Out'),
)
...
status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=AVAILABLE)
Then, you can run your checks like:
if self.status == self.STATUS_AVAILABLE:
self.status = self.STATUS_CHECKED_OUT
You could use strings and a CharField instead if you like, as well. The key is to decouple the static text from your code, which allows much greater flexibility in your app going forward.
Next, history needs to be a ManyToManyField. Right now, your "history" is only who last checked the item out or what the last item the user checked out was, and as a result is pretty useless.
class ItemObject(models.Model):
...
history = models.ManyToManyField(User, through='ItemHistory', related_name='item_history', blank=True)
class ItemHistory(models.Model):
CHECKED_OUT = 1
RETURNED = 2
ACTIVITY_CHOICES = (
(CHECKED_OUT, 'Checked Out'),
(RETURNED, 'Returned'),
)
item = models.ForeignKey(ItemObject)
user = models.ForeignKey(User)
activity = models.PostiveIntegerField(choices=ACTIVITY_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-timestamp'] # latest first
Which then allows you to get full histories:
some_item.history.all()
some_user.item_history.all()
To add a new history, you would do:
ItemHistory.objects.create(item=some_item, user=some_user, activity=ItemHistory.CHECKED_OUT)
The auto_now_add attribute ensures that the timestamp is automatically set when the relationship is created.
You could then actually get rid of the last_checkout and last_activity fields entirely and use something like the following:
class ItemObject(models.Model):
...
def _last_checkout(self):
try:
return self.history.filter(activity=ItemHistory.CHECKED_OUT)[0].timestamp
except IndexError:
return None
last_checkout = property(_last_checkout)
def _last_activity(self):
try:
return self.history.all()[0].timestamp
except IndexError:
return None
last_activity = property(_last_activity)
And, you can then use them as normal:
some_item.last_checkout
Finally, your checkout method is not an override of save so it's not appropriate to call super(ItemObject, self).save(). Just use self.save() instead.
I just started to learn Django and I had a question.
I'm trying to automatically add the missing information, when saving form data. I get to change/add the desired "cleaned_data" information by overriding save() method of modelform class, but changes are not recorded in the database. Actually, how to write the modified information? This is code:
def save(self, commit = True, *args, **kwargs):
temp = ServiceMethods(url = self.cleaned_data.get('url'), wsdl_url = self.cleaned_data.get('wsdl_url'))
if not temp.get_wsdl_url():
temp.make_wsdl_url()
if temp.get_wsdl_url():
temp.make_wsdl()
self.cleaned_data['wsdl_url'] = temp.get_wsdl_url()
self.cleaned_data['wsdl_description'] = temp.get_wsdl_description()
super(ServiceForm, self).save(commit = commit, *args, **kwargs)
And model:
class Services(models.Model):
name = models.CharField('Имя', max_length=256)
url = models.URLField('Ссылка', unique = True)
wsdl_url = models.URLField('Ссылка на WSDL-документ', blank=True)
description = models.TextField('Описание сервиса',blank=True)
wsdl_description = models.TextField('WSDL описание', blank=True, editable=False)
added = models.DateTimeField('Добавлено', auto_now_add=True)
TIA
Try setting the data on self.instance instead of in self.cleaned_data, and let me know if that works.