Making an object unsaveable ...

72 views
Skip to first unread message

Bernd Wechner

unread,
Mar 4, 2018, 4:43:06 AM3/4/18
to django...@googlegroups.com

An interesting question I've not been able to solve reading docs or Googling. As every posting here only after some serious research efforts including class inspections ;-). But am stumped and wonder if anyone else has any ideas.

Here is the use case:

  1. Load an object from database
  2. Flag it as unsaveable, not to be written back to database under any circumstances
  3. Make changes to the object (its fields)
  4. Present a detail view of the object.

Basically, would like the freedom to change an object knowing the database is secure.

As ever, am more interested in how to do this, than questioning why to do it. It may not be possible in which case fear not, I'm on top of the why's and can look at alternative strategies. But this use case provides an easy way of injecting some filters in one place of a code body I have without changing any of the views or other code. Simply one small hook I place into extant code, which will play around with the object (making translations for example). But in order to do so would want to know this cannot be written back to the database, that from here on in, it's for display purposes only.

Have a suspicion after some research that this can't be done. In which case there's a second and almost as useful use case:

  1. Load an object from database
  2. Make a copy, that is unsaveable, not to be written back to database under any circumstances
  3. Make changes to the copy (its fields)
  4. Present a detail view of the copy.

This has some slightly better chance of being possible. If not as well, yes indeed, I have to dive into the view itself and put hooks into each field as it renders I guess. Which is life. But would seem useful to do it with one of these use cases (again no need to discuss the merits of one approach over the other at all, am more interested in possibility - projects have huge amounts of context not readily shareable that influence the relative merits of one approach over another of course.

Regards,

Bernd.

Dan Tagg

unread,
Mar 4, 2018, 6:24:38 AM3/4/18
to django...@googlegroups.com
Two possibilities spring to mind

1. Don't grant django's database user the right to update the relevant table. If you take this approach you'll need to catch any errors if anyone writes code to attempt to do this

2. Use signals to catch any save attempts. This is easily circumvented. 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/9162bad4-935a-ed39-d2d8-a001103ec705%40gmail.com.
For more options, visit https://groups.google.com/d/optout.



--
Wildman and Herring Limited, Registered Office: 28 Brock Street, Bath, United Kingdom, BA1 2LN, Company no: 05766374

Bernd Wechner

unread,
Mar 4, 2018, 7:45:38 PM3/4/18
to Django users
Thanks Dan,

I was actually thinking also last night of:

3. Override the save() method on the model.

Not tested, not sure how robust, but as it happens there's a hook there already as all the models I'm work with have it overridden with tiny method that updates some admin fields like last_updated and last_updated_by. This could be a nice place for a small hook.

Signals are an idea too, and want to explore them as much for the heck of it and learning as anything. But I wonder what you mean by "This is easily circumvented." - I'm guessing that even if you catch a signal prior to save and do something, there's no guarantee some downstream code won't do a save. I wonder if overriding save() is more secure, or if there's any possibility other code could go around such an override.

Regards,

Bernd.


On Sunday, 4 March 2018 22:24:38 UTC+11, Dan Tagg wrote:
Two possibilities spring to mind

1. Don't grant django's database user the right to update the relevant table. If you take this approach you'll need to catch any errors if anyone writes code to attempt to do this

2. Use signals to catch any save attempts. This is easily circumvented. 
On 4 March 2018 at 09:41, Bernd Wechner <bernd....@gmail.com> wrote:

An interesting question I've not been able to solve reading docs or Googling. As every posting here only after some serious research efforts including class inspections ;-). But am stumped and wonder if anyone else has any ideas.

Here is the use case:

  1. Load an object from database
  2. Flag it as unsaveable, not to be written back to database under any circumstances
  3. Make changes to the object (its fields)
  4. Present a detail view of the object.

Basically, would like the freedom to change an object knowing the database is secure.

As ever, am more interested in how to do this, than questioning why to do it. It may not be possible in which case fear not, I'm on top of the why's and can look at alternative strategies. But this use case provides an easy way of injecting some filters in one place of a code body I have without changing any of the views or other code. Simply one small hook I place into extant code, which will play around with the object (making translations for example). But in order to do so would want to know this cannot be written back to the database, that from here on in, it's for display purposes only.

Have a suspicion after some research that this can't be done. In which case there's a second and almost as useful use case:

  1. Load an object from database
  2. Make a copy, that is unsaveable, not to be written back to database under any circumstances
  3. Make changes to the copy (its fields)
  4. Present a detail view of the copy.

This has some slightly better chance of being possible. If not as well, yes indeed, I have to dive into the view itself and put hooks into each field as it renders I guess. Which is life. But would seem useful to do it with one of these use cases (again no need to discuss the merits of one approach over the other at all, am more interested in possibility - projects have huge amounts of context not readily shareable that influence the relative merits of one approach over another of course.

Regards,

Bernd.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Derek

unread,
Mar 5, 2018, 1:29:01 AM3/5/18
to Django users

I think the second case is more manageable; I'd actually have a second table that has an exact duplicate structure of the first. Then you can save your newly changed copy into that -- and delete the record just afterward, or even  much later,  if you need to.

I cannot imagine a scenario in which somone would do a lot of work and then not save it (even temporarily).

bill.torcaso

unread,
Mar 5, 2018, 9:48:02 AM3/5/18
to Django users

Matthew Pava

unread,
Mar 5, 2018, 11:15:17 AM3/5/18
to django...@googlegroups.com

You can always override the save method on the model.  If you need to make all your models override the save method, use inheritance.

 

class DoNotSaveModel(models.Model):

     def save(*args, **kwargs):

        pass

--

You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Melvyn Sopacua

unread,
Mar 6, 2018, 3:13:42 AM3/6/18
to django...@googlegroups.com
Hi Bernd,

On zondag 4 maart 2018 10:41:52 CET Bernd Wechner wrote:

> Here is the use case:
>
> 1. Load an object from database
> 2. Flag it as unsaveable, not to be written back to database under any
> circumstances
> 3. Make changes to the object (its fields)
> 4. Present a detail view of the object.

Since this is for display purposes, why does it have to remain a model?
--
Melvyn Sopacua

James Bennett

unread,
Mar 6, 2018, 3:28:58 AM3/6/18
to django...@googlegroups.com
The only way to guarantee a model is unsaveable is to do it at the database permission level.

You can override methods on the model in your Python code to make it *harder* to save, but you can't make it impossible, because of how Python works.

Consider the following simplified pair of classes:


class Base:
    def __init__(self, name):
        self.name = name

    def save(self):
        print("Saving {}".format(self.name))


class Child(Base):
    def save(self):
        raise Exception("Can't save me!")


You'd think you can't save() an instance of Child. Except:


c = Child(name="Unsaveable instance")
Base.save(c)


Which will succeed and print that it's saving the "unsaveable" instance of Child. Now imagine that you override save() and everything else you can think of on your model class, and then someone goes and grabs the base Model class and calls its save() method passing in an instance of your "unsaveable" class. It's going to succeed in saving your "unsaveable" model.

No matter how much you do to your model class in Python, someone who's sufficiently determined will always be able to find a way to save it. Only by cutting off the ability to run INSERT/UPDATE queries at the database-permission level can you completely stop this. 



--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Bernd Wechner

unread,
Mar 7, 2018, 8:13:08 PM3/7/18
to Django users
Melvyn,

Just preference. Nothing more. Fix the issue at its source so all views that exist or might exist honor the demand basically. One model, many views.

Regards,

Bernd.

Bernd Wechner

unread,
Mar 7, 2018, 8:29:53 PM3/7/18
to Django users
James,

Thanks, that's really well put.

I've implemented an override, and better still I love that with a model mix in I can override the save method on the object. Sort of like:

class MyMixIn:
   
def check_stuff(self)
         
...
         
if ...:
             
self.save = self.disabled_save

   
def disabled_save(self):

       
raise Exception("Can't save me!")

class MyModel(MyMixIn, models.Model):
   
...

Now yes, your insight that this can be circumvented is interesting, and it might (though I wonder how in this version, perhaps by calling models.Model.save(obj)?) but am content with a solution for now that prevents inadvertent saving not malicious saving efforts. What this allows is that the extant body of code can tested and run and even put into production in a sense, trusting that any overlooked save avenues will throw this exception. Which is awesome. 

Bot 100% secure perhaps, but pretty danged good. I've tested this sort of thing now and walked the code in a debugger and am pretty happy with it. As long as there are not other ways the django core code might go and save an object (i.e. not using a models save() method. I sort of imagine and hope not that one of the deign paradigms was in fact to make something like this possible, to override base model methods with confidence (typically invoking super() to extend on existing functions but perhaps not as in here).

Anyhow, rather pleased this works. Opens up a pile of interesting options (the main interest I have is in loading an object, modifying an object for display purposes and sandbox purposes, but securing against an inadvertent save of the object).

Regards,

Bernd.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
Reply all
Reply to author
Forward
0 new messages