Change Field of Foreign Key Object - django

I have the following problem.
I have a model Towar:
class Towar(models.Model):
nrSeryjny = models.CharField(max_length=100)
opis = models.CharField(max_length=255)
naStanie = models.NullBooleanField(null=True)
def __unicode__(self):
return "%s" % self.opis
def lowerName(self):
return self.__class__.__name__.lower()
def checkState(self):
return self.naStanie
def changeState(self,state):
self.naStanie=state
class Meta:
ordering=['nrSeryjny']
app_label = 'baza'
permissions=(("view_towar","mozna miec podglad dla towar"),)
and another model, Wypozyczenie, which is linked to Towar by a foreign key relationship:
class Wypozyczenie(models.Model):
dataPobrania = models.DateField()
pracownik = models.ForeignKey(User,null=True)
kontrahent = models.ForeignKey(Kontrahenci,null=True)
towar = models.ForeignKey(Towar,null=True)
objects = WypozyczenieManager()
default_objects = models.Manager()
ZwrotyObjects = WypozyczenieZwrotyManager()
def lowerName(self):
return self.__class__.__name__.lower()
def __unicode__(self):
if self.towar == None:
return "Dla:%s -- Kto:%s -- Kiedy:%s -- Co:%s" % (self.kontrahent,self.pracownik,self.dataPobrania,"Brak")
else:
return "Dla:%s -- Kto:%s -- Kiedy:%s -- Co:%s" % (self.kontrahent,self.pracownik,self.dataPobrania,self.towar)
class Meta:
ordering = ['dataPobrania']
app_label = 'baza'
permissions = (("view_wypozyczenie","mozna miec podglad dla wypozyczenie"),)
and a view to add models:
def modelAdd(request,model,modelForm):
mod = model()
if request.user.has_perm('baza.add_%s' % mod.lowerName()):
if request.method == 'POST':
form=modelForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/'+ mod.lowerName() + '/')
else:
form = modelForm()
v = RequestContext(request,{'form':form})
return render_to_response('add_form.html',v)
What I want is that when I add Wypozyczenie and save it, then the Towar that is stored by Wypozyczenie changes its naStanie field from True to False.
Greets

If you want to always keep those two in sync you can override Wypozyczenie's save() method.
class Wypozyczenie(models.Model):
...
def save(self):
self.towar.naStanie = False
self.towar.save()
Alternatively, you can also override ModelForm's save() method.

What have you tried?
Can't you just put
myinstance = form.save()
towar = myinstance.towar
towar.naStanie = False
toware.save()
instead of your simple call to form.save() in your view.

You can use signals emitted when saving your Wypozyczenie object. It might be a little "cleaner" than overriding save(), especially when it's useful to re-use the function for other models:
#receiver(models.signals.post_save, sender=Wypozyczenie)
def after_wypozyczenie_save(sender, instance, created, **kwargs):
# `instance` is your saved Wypozyczenie object, you can
# access all it's fields here:
instance.towar.naStanie = False
instance.towar.save()
# It's also possible to do different action on the first save
# and on subsequent updates:
#
# if created:
# ...
# else:
# ...
There are other signals sent before saving or on deletion. Django documentation on signals is quite helpful here.

Related

Inconsistent behavoiur of save(commit=False) for a ModelForm in Django 2.2

I have a ModelForm that selects one field and excludes the rest, one of which is a ForeignKey that I need to populate manually before saving the form.
I'm in the process of fixing a bug that one of my views has and I'm making use of the request.session object to store information so that the GET and POST method funcions will be in synced by the session in locating the model at hand instead of separately iterating though the database to pin-point what model object the submitted data should be saved for.
I'm making use of the form_object.save(commit=False) funcionality in other places of the same view and it works as I need but for some reason there is a section of the view where it just doesn't populate the extra field before calling the eventual save() method and I get an IntegrityError for that column in the database, even if it is not null=False in my model declaration right now (and I think it'd rather should be..).
So here's my ModelForm:
class PesoFormApertura(ModelForm):
peso_apertura = forms.DecimalField(min_value=0,required=True)
class Meta:
model = Peso
fields = ('peso_apertura',)
here's the Model itself:
class Peso(models.Model):
prodotto = models.ForeignKey(ProdottoPesato,on_delete=models.CASCADE)
peso_apertura = models.DecimalField(max_digits=8,decimal_places=2, blank=False, null=True)
peso_calcolato = models.DecimalField(max_digits=8,decimal_places=2, blank=True, null=True)
peso_chiusura = models.DecimalField(max_digits=8,decimal_places=2, blank=True, null=True)
data = models.DateField(auto_now_add=True, blank = False)
def __str__(self):
return "{} - {} - {}".format(self.prodotto.nome, self.prodotto.get_categoria_display(), self.data)
def save(self, *args, **kwargs):
if self.peso_apertura == 0:
prodotto_associato = ProdottoPesato.objects.get(pk = self.prodotto.pk)
if prodotto_associato.permanente == False:
prodotto_associato.delete()
self.delete()
else:
super().save(*args, **kwargs)
else:
super().save(*args, **kwargs)
class Meta:
ordering = ['prodotto']
and here's the view part where the save() method is failing (where I placed a comment):
if not 'prodotto-da-correggere' in request.session:
for prodotto in tutti_prodotti:
pesi_questo_prodotto_oggi = prodotto.pesi_di_oggi()
for peso in pesi_questo_prodotto_oggi:
if peso.peso_apertura == None:
prodotto_da_elaborare = prodotto
peso_del_giorno = peso
break
if prodotto_da_elaborare:
finito = False
instance_peso = peso_del_giorno
form = PesoFormApertura(request.POST, instance = instance_peso)
if form.is_valid():
form.save(commit=False) # WORKS FINE
form.prodotto = prodotto_da_elaborare
form.save()
else:
form = PesoFormApertura(request.POST)
else: # HERE IS WHERE IT DOESN'T BEHAVE LIKE ABOVE
prodotto_da_elaborare = ProdottoPesato.objects.get(id=request.session['prodotto-da-correggere'])
peso_del_giorno = None
for peso in prodotto_da_elaborare.pesi_di_oggi():
if peso.peso_apertura == None or peso.peso_apertura > 0:
peso_del_giorno = peso
break
form_correzione = PesoFormApertura(request.POST, instance = peso_del_giorno)
if form_correzione.is_valid():
form_correzione.save(commit=False)
form_correzione.prodotto = prodotto_da_elaborare
form_correzione.save() # IT DOESN'T ADD THE INFORMATION TO THE COLUMN WHEN IT SAVES
request.session.flush()
The save() method works alright for the first part of the IF statement and just doesn't work for the rest of the snippet in the ELSE section.
So I'm wondering if the session has something to do with this.
When you call form.save() it returns the instance. You should modify and save that instance.
if form_correzione.is_valid():
peso = form_correzione.save(commit=False)
peso = prodotto_da_elaborare
peso.save()
You should change the other part of the view to use this approach as well. It looks like form.prodotto = prodotto_da_elaborare is having no effect, but you haven't noticed because prodotto is already set so you don't get an integrity error.

How can I customize default to django REST serializer

http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
I wants to read default value from userprofile automatically. Right now the offical method support User and DateTime. But I want my customized value. How can I do that?
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
This is my workaround. Copy the example from source code and place it here.
Hope near future it has friendly solution.
class CurrentBranchDefault:
def set_context(self, serializer_field):
self.user = serializer_field.context['request'].user
self.branch = self.user.userprofile.selected_branch
def __call__(self):
return self.branch
def __repr__(self):
return unicode_to_repr('%s()' % self.__class__.__name__)
class StaffOrderSerializer(serializers.ModelSerializer):
branch = serializers.HiddenField(default=CurrentBranchDefault())
If you want to calculate one hidden field, using other incoming fields in serializer,
than you need to use serializer_field.context['request'].data
This "data" will be validated before "set_context()", so you can use it in safe.
I hope it will help someone else.
class DefineNoteType:
def set_context(self, serializer_field):
# setting field "type", calculated by other serializer fields
data = serializer_field.context['request'].data
subscriber = data.get('subscriber', None)
connection = data.get('connection', None)
if subscriber:
self.type = 'subscriber_type'
elif connection:
self.type = 'connection_type'
else:
raise serializers.ValidationError('Custom error.')
def __call__(self):
return self.type
class NoteSerializer(serializers.ModelSerializer):
type = serializers.HiddenField(default=DefineNoteType())

How to get admin inline name or how to define which inline is changed?

I have 2 inlines with the same model.
class UsersInlineAdmin(GenericTabularInline):
model = CTSportsToCriterias
class TeamsInlineAdmin(GenericTabularInline):
model = CTSportsToCriterias
class CTSportsToCriterias(models.Model):
user_use = models.BooleanField(verbose_name=_('is available to users?'), default=False)
team_use = models.BooleanField(verbose_name=_('is available to teams?'), default=False)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
criteria_group = models.ForeignKey(CriteriaGroups, verbose_name=_('criteria group'))
When I remove an item from UsersInlineAdmin (so in this case user_use is True) and if this item also exists in TeamsInlineAdmin (team_use is also True), django automatically removes this item from TeamsInlineAdmin inline, because according to the Django docs, the code:
def save_formset(self, request, form, formset, change):
for obj in formset.deleted_objects:
...
provides model's object.
So, because in my case the model can have some options (user_use\ team_use), is it possible to get inline names in save_formset in order to do something like:
if (inline_name == 'UsersInlineAdmin' and not obj.team_use) or \
(inline_name == 'TeamsInlineAdmin' and not obj.user_use):
obj.delete()
?
EDIT:
If I add form and override clean(), when I add a user item in case when team s item already exists, Django does not update in DB user_use to True. Instead Django creates a new record.
# admin
class UsersInlineAdmin(GenericTabularInline):
model = CTSportsToCriterias
form = UserFieldForm
# form
class UserFieldForm(forms.ModelForm):
class Meta:
model = CTSportsToCriterias
def clean(self):
m = super(UserFieldForm, self).clean()
# This trick works ok in case when I want to DELETE user's item,
# so the trick changes in DB 'user_use' to False, if 'team_use' already exists
if m['DELETE'] is True:
try:
qs = CTSportsToCriterias.objects.get(object_id=object_id,
content_type=content_type,
criteria_group=criteria_group,
team_use=True)
m['id'] = qs.pk
m['DELETE'] = m['user_use'] = False
except ObjectDoesNotExist:
pass
# Can not repeate the same trick as above.
# When 'team_use' already exists, I want to change the value 'user_use' of this team's obj to True,
# but the trick as above does not work in this case - Django adds a new row to DB, instead of update
# the existing one.
elif m['DELETE'] is False:
try:
qs = CTSportsToCriterias.objects.get(object_id=object_id,
content_type=content_type,
criteria_group=criteria_group,
team_use=True)
m['id'] = qs.pk
m['DELETE'] = m['user_use'] = False
except ObjectDoesNotExist:
pass
return m
# forms
class UserFieldForm(forms.ModelForm):
user_use = forms.BooleanField(widget=forms.HiddenInput(), initial=True, required=True)
def clean(self):
m = super(UserFieldForm, self).clean()
if m['DELETE'] is True:
m['user_use'] = False
return m
class TeamFieldForm(forms.ModelForm):
team_use = forms.BooleanField(widget=forms.HiddenInput(), initial=True, required=True)
def clean(self):
m = super(TeamFieldForm, self).clean()
if m['DELETE'] is True:
m['team_use'] = False
return m
# admin
class UsersInlineAdmin(GenericTabularInline):
model = CTSportsToCriterias
form = UserFieldForm
class TeamsInlineAdmin(GenericTabularInline):
model = CTSportsToCriterias
form = TeamFieldForm
class CriteriaGroupsAdmin(admin.ModelAdmin):
inlines = [CriteriaTranslationsInlineAdmin, UsersInlineAdmin, TeamFieldForm]
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for obj in formset.deleted_objects:
if isinstance(instance, CTSportsToCriterias):
is_user_use_form = obj.user_use
is_team_use_form = obj.team_use
# There is just one option in DB (user_use or team_use), so delete obj from DB
if not is_user_use_form and not is_team_use_form:
obj.delete()
else:
user_n_team = CTSportsToCriterias.objects.get(
object_id=obj.object_id, content_type=obj.content_type, criteria_group=obj.criteria_group)
# Means 'user_use' exists and we need to change 'team_use' to False
if is_user_use_form:
user_n_team.team_use = False
# Means 'team_use' exists and we need to change 'user_use' to False
elif is_team_use_form:
user_n_team.user_use = False
user_n_team.save()
# Saves user_use + team_use in one row as there is no need to use 2 DB rows for each case (user\team)
# if user_use or team_use already exists in DB, or just creates a new row.
for instance in instances:
if isinstance(instance, CTSportsToCriterias):
content_type = ContentType.objects.get(id=instance.content_type_id)
criteria_group = CriteriaGroups.objects.get(id=instance.criteria_group_id)
qs_kwargs = dict(content_type=content_type, object_id=instance.object_id, criteria_group=criteria_group)
is_user_use = instance.user_use
is_team_use = instance.team_use
if is_user_use:
qs_kwargs.update(defaults=dict(user_use=is_user_use))
if is_team_use:
qs_kwargs.update(defaults=dict(team_use=is_team_use))
CTSportsToCriterias.objects.update_or_create(**qs_kwargs)

query in django model

I am using django and as I am pretty new I have some questions.
I have one model called Signatures and a ModelForm called SignatureForm in my models.py file:
class Signature(models.Model):
sig = models.ForeignKey(Device)
STATE = models.CharField(max_length=3, choices=STATE_CHOICES)
interval = models.DecimalField(max_digits=3, decimal_places=2)
verticies = models.CharField(max_length=150)
class SignatureForm(ModelForm):
class Meta:
model = Signature
widgets = {
'verticies': HiddenInput,
}
To use it, I wrote the following function in views.py:
def SigEditor(request):
# If the form has been sent:
if request.method == 'POST':
form = SignatureForm(request.POST)
# If it is valid
if form.is_valid():
# Create a new Signature object.
form.save()
return render_to_response('eQL/sig/form_sent.html')
else:
return render_to_response('eQL/sig/try_again.html')
else:
form = SignatureForm()
return render_to_response('eQL/sig/showImage.html', {'form' : form})
However, I don't want to save all the new signatures. I mean, if the user introduces a new signature of the device A and state B, I would like to check if I have some signature like that in my database, delete it and then save the new one so that I have only one signature saved for each device and state.
I have tried something like this before saving it but of course is not working:
q = Signature.objects.filter(sig = s, STATE = st)
if q.count != 0:
q.delete()
form.save()
can anyone help?? thanks!!
If you really do want to delete, why not?
Signature.objects.filter(sig=s, STATE=st).delete()
If you only ever want one combination of those items, you could use get_or_create, and pass in the instance to your ModelForm.
instance, created = Signature.objects.get_or_create(sig=s, STATE=st)
form = SignatureForm(request.POST, instance=signature)
# edit instance.
Or put it in your form save logic:
class SignatureForm(ModelForm):
def save(self, *args, **kwargs):
data = self.cleaned_data
instance, created = Signature.objects.get_or_create(sig=data['sig'], STATE=data['state'])
self.instance = instance
super(SignatureForm, self).save(*args, **kwargs)

Django - Overriding get_or_create with models.py

I have a class in which I want to override the get_or_create method. Basically if my class doesn't store the answer I want it do some process to get the answer and it's not provided. The method is really a get_or_retrieve method. So here's the class:
class P4User(models.Model):
user = models.CharField(max_length=100, primary_key=True)
fullname = models.CharField(max_length=256)
email = models.EmailField()
access = models.DateField(auto_now_add=True)
update = models.DateField(auto_now_add=True)
#classmethod
def get_or_retrieve(self, username, auto_now_add=False):
try:
return self.get(user=username), False
except self.model.DoesNotExist:
import P4
import datetime
from django.db import connection, transaction, IntegrityError
p4 = P4.P4().connect()
kwargs = p4.run(("user", "-o", username))[0]
p4.disconnect()
params = dict( [(k.lower(),v) for k, v in kwargs.items()])
obj = self.model(**params)
sid = transaction.savepoint()
obj.save(force_insert=True)
transaction.savepoint_commit(sid)
return obj, True
except IntegrityError, e:
transaction.savepoint_rollback(sid)
try:
return self.get(**kwargs), False
except self.model.DoesNotExist:
raise e
def __unicode__(self):
return str(self.user)
Now I completely admit that I have used the db/models/query.py as my starting point. My problem is this line.
obj = self.model(**params)
I am able to get the params but I haven't defined self.model. I don't understand what that needs to be and it's not intuitively obvious what value that should be. Even looking back at the query.py I can't figure this out. Can someone explain this to me? I would really like to understand it and fix my code.
Thanks
get_or_create is a Manager method, that is you access it via model.objects - it is the manager class that has an attribute model. So maybe the easiest thing to do would be to create a custom Manager and put your method there.
However, fixing your code as it stands is easy. self.model is just the classname - that line is simply instantiating the class with the given parameters. So you could just do
obj = P4User(**params)
although this breaks if you subclass the model.
Daniel was right in his suggestion to use a Manager class. Here is what I ended up with.
# Managers
class P4Manager(models.Manager):
def p4_run_command(self, command):
"""Runs a basic perforce command and return the values"""
p4 = P4.P4()
p4.connect()
values = p4.run(command)
p4.disconnect()
return self.__unify_key_values__(values)
def __unify_key_values__(self, args):
"""Unified method to clean up the lack of standard returns from p4 api"""
final = []
for item in args:
params = dict( [(k.lower(),v) for k, v in item.items()])
results = {}
for k, v in params.items():
if k in ['password', ]: continue
if k in ["access", "update"]:
v = datetime.datetime.strptime(v, "%Y/%m/%d %H:%M:%S")
results[k]=v
final.append(results)
return final
def __get_or_retrieve_singleton__(self, **kwargs):
"""This little sucker will retrieve a key if the server doesn't have it.
In short this will go out to a perforce server and attempt to get a
key if it doesn't exist.
"""
assert len(kwargs.keys())==2, \
'get_or_retrieve() must be passed at one keyword argument'
callback = kwargs.pop('callback', None)
try:
return self.get(**kwargs), False
except self.model.DoesNotExist:
params = self.p4_run_command((kwargs.keys()[0], "-o", kwargs.values()))
if callback:
params = callback(*params)
obj = self.model(**params)
sid = transaction.savepoint()
obj.save(force_insert=True)
transaction.savepoint_commit(sid)
return obj, True
except IntegrityError, e:
transaction.savepoint_rollback(sid)
try:
return self.get(**kwargs), False
except self.model.DoesNotExist:
raise e
class P4UserManager(P4Manager):
"""
A Generic User Manager which adds a retrieve functionality
"""
def get_or_retrieve(self, user):
kwargs = { 'callback' : self.__userProcess__ ,
'user': user }
return self.__get_or_retrieve_singleton__(**kwargs)
def __userProcess__(self, *args):
args = args[0]
if not args.has_key('access'):
raise self.model.DoesNotExist()
return args
# Models
class P4User(models.Model):
"""This simply expands out 'p4 users' """
user = models.CharField(max_length=100, primary_key=True)
fullname = models.CharField(max_length=256)
email = models.EmailField()
access = models.DateField(auto_now_add=True)
update = models.DateField(auto_now_add=True)
objects = P4UserManager()
def __unicode__(self):
return str(self.user)
I hope other find this usefull
Use self instead of self.model.
The code you are copying from, is method for class Queryset. There, self.model is the Model whose queryset is intended to be used. Your method is classmethod of a model itself.