Django saving to ManyToMany fields - django

I have a simple model class with 2 ManyToManyField fields like this:
models.py
class Folder(models.Model):
user = models.ManyToManyField(User)
asset = models.ManyToManyField(Asset)
In my view, I know the user ID and the asset ID. Say the user ID is 1 and the asset ID is 30, how do I inject this row? I guess I don't understand how to instantiate Folder so I can save/update the row.
views.py
def addAssetToMyFolder(request, id=None):
''' view is simplified for brevity
'''
f = Folder(
user = 1,
asset = 30,
)
f.save()

To associate a user or asset instance with a folder you need to first save the folder.
To store a many to many relationship the database creates a third table which stores the ids of the objects.
So if you want to relate a user to a folder as a many to many relationship, both of them should have their own ids before they can be related as many to many.
Say you have two users with ids 10 and 19 respectively.
You have one folder with id 4 and user 10 and user 19 are related to this folder. At the db level this how these relations will be stored
folder_id user_id
4 10
4 19
so for each many to many relation there is one row in the relations table for the two models.
Same will be valid for asset.
So the code should be changed to:
def addAssetToMyFolder(request, id=None):
''' view is simplified for brevity
'''
f = Folder()
f.save()
user = User.objects.get(id=1) # not needed if adding by id
f.user.add(user) # or f.user.add(user_id)
asset = Asset.objects.get(id=30) # not needed if adding by id
f.asset.add(asset) # or f.asset.add(asset_id)
check out : https://docs.djangoproject.com/en/1.6/topics/db/examples/many_to_many/

Because I reallllly hate redundancy, here's a another solution using a dynamic modelform. The benefits are it's neater, you don't need to fetch the User and Asset objects, you use the related pk and you only save once.
The drawback is that it's overkill for the common everyday need. So you should probably mark #zaphod100.10 answer as correct, but know that this method also exists:
Meta = type('Meta', (), {'model': Folder, 'fields': ['user', 'asset']} )
FolderForm = type('FolderForm', (forms.ModelForm, ), {'Meta': Meta})
data = {'user': ['1'], 'asset': ['30']} #the values need to be a list of strings, representing pks of related objects
f = FolderForm(data)
new_obj = f.save()

Related

Is it possible to link multiple models to one fiel in django?

Let's say I have these models:
class Material(models.Model):
name = models.CharField([...])
class Consumable(models.Model):
name = models.CharField([...])
restores = models.IntegerField([...])
class Weapon(models.Model):
name = models.CharField([...])
damage = models.IntegerField([...])
# And then I have an 'inventory', like this one:
class Inventory(models.Model):
user = models.ForeignKey([...]) # to which user you want to link the item
item = models.ForeignKey([...]]) # which item
quantity = models.IntegerField([...]) # how many of it
I want to be able to have all Material, Consumable, and Weapon models listed in the 'item' field, so when you want to add an item as an inline, you would see all 3 models' objects.
Something like
# instead of this
item = models.ForeignKey(Consumable) # which item
# want something like this
item = models.ForeignKey(Consumable and Material and Weapon) # which item
# this wouldn't work ofc...
Is there a way to collect all 3 of them and pass them to the 'item' field, without the need of restarting the server? (when making a "choices" list that queries from a model you must restart the server to see the newly added objects, I don't want that.)
I also want to stick to the built-in admin of Django since it provided everything I need for the past couple of months, but I am open to any ideas.
I could be wrong but I think you are making this more complex than it needs to be. Instead of doing separate classes for materials (type of material) and consumable (type of product), you can have that built in the last class as model field as category or bolean fields.
class Products(models.Model):
material_type =
consumable = boolean for yes no or you can do multiple choice field
Then for items you can query the number of items based on material_type or consumable model fields (see query filters for for more).
all_items = Products.model.all()
consumable_items = Products.model.filter(your filter logic goes here)
Hope this helps!

Is it possible to write a QuerySet method that modifies the dataset but delays evaluation (similar to prefetch_related)?

I'm working on a QuerySet class that does something similar to prefetch_related but allows the query to link data that's in an unconnected database (basically, linking records from django apps's database to records in a legacy system, using a shared unique key, something along the links of:
class UserFoo(models.Model):
''' Uses the django database & can link to User model '''
user = models.OneToOneField(User, related_name='userfoo')
foo_record = models.CharField(
max_length=32,
db_column="foo",
unique=True
) # uuid pointing to legacy db table
#property
def foo(self):
if not hasattr(self, '_foo'):
self._foo = Foo.objects.get(uuid=self.foo_record)
return self._foo
#foo.setter
def foo(self, foo_obj):
self._foo = foo_obj
and then
class Foo(models.Model):
'''Uses legacy database'''
id = models.AutoField(primary_key=True)
uuid = models.CharField(max_length=32) # uuid for Foo legacy db table
…
#property
def user(self):
if not hasattr(self, '_user'):
self._user = User.objects.get(userfoo__foo_record=self.uuid)
return self._user
#user.setter
def user(self, user_obj):
self._user = user_obj
Run normally, a query that matches 100 foos (each with, say, 1 user record) will end up requiring 101 queries: one to get the foos, and a hundred for each user record (by doing a look up for the user record by calling the user property on each food).
To get around this, I am making something similar to prefetch_related which pulls all of the matching records for a query by the key, which means I just need one additional query to get the remaining records.
My code looks something like this:
class FooWithUserQuerySet(models.query.QuerySet):
def with_foo(self):
qs = self._clone()
foo_idx = {}
for record in self.all():
foo_idx.setdefault(record.uuid, []).append(record)
users = User.objects.filter(
userfoo__foo_record__in=foo_idx.keys()
).select_related('django','relations','here')
user_idx = {}
for user in users:
user_idx[user.userfoo.foo_record] = user
for fid, frecords in foo_idx.items():
user = user_idx.get(fid)
for frecord in frecords:
if user:
setattr(frecord, 'user', user)
return qs
This works, but any extra data saved to a foo is lost if the query is later modified — that is, if the queryset is re-ordered or filtered in any way.
I would like a way to create a method that does exactly what I am doing now, but waits until the moment that adjusts whenever the query is evaluated, so that foo records always have a User record.
Some notes:
the example has been highly simplified. There are actually a lot of tables that link up to the legacy data, and so for example although there is a one-to-on relationship between Foo and User, there will be some cases where a queryset will have multiple Foo records with the same key.
the legacy database is on a different server and server platform, so I can't link the two tables using a database server itself
ideally I'd like the User data to be cached, so that even if the records are sorted or sliced I don't have to re-run the foo query a second time.
Basically, I don't know enough about the internals of how the lazy evaluation of querysets works in order to do the necessary coding. I have jumped back and forth on the source code for django.db.models.query but it really is a fairly dense read and I'm hoping someone out there who's worked with this already can offer some pointers.

How to make a filter so as to have two entries for each user?

I have model:
class MyModel(models.Model):
user = models.ForeignKey(user)
data = models.IntegerField()
created = models.DateTimeField(default=datetime.now)
[...]
How to make a filter so as to have result of 10 entries with two for each user and then all sorted by "user"?
Example:
Display page "users entries" where are the two latest entries per user.
I'm a django newbe, but recently I had similar problem.
Maybe it will make it for You.
I had users based on django User model and Movies with field owner. I wanted to display last 2 movies of every user, grouped by users and sorted by movie publication date.
What I did, was:
Create and add method to User model (it returns list of 2 movies of user):
from django.contrib import auth
def get_users_last_movies(self):
movies = Movies.objects.filter(state=3,
pubdate__lte=datetime.now(),
owner=self).order_by('-pubdate')[0:2]
return movies
auth.models.User.add_to_class('get_users_last_movies', get_users_last_movies)
in view file select all Users your interested in and append their movies to them, use sort method on created user list
dusers = User.objects.filter(is_active=True)
users = []
for duser in dusers:
duser.movies = duser.get_users_last_movies()
users.append(duser)
users.sort(key=lambda x: x.movies[0].pubdate, reverse=True)
data['dusers'] = users
Hope that this code will get You on track

How to give choices from values of another field in Django models

I have a form, which is for scheduling an appointment. I give user 3 dates on which the meeting can be scheduled. Now in the admin I want to select one of the dates according to my convenience, and store it in a field of the same model. How can I do that
Right now my meeting dates are just char fields like this
schedule1 = models.CharField()
schedule2 = model.CharField()
schedule3 = models.CharFiedl()
selected_schedule = model.CharField(choices={something here})
The schedule fields will be filled when the object is created. So I am sure the choices will be there, I just have to dynamically set them. How can I do this?
Any help will be appreciated.
Here's what you do (if the schedule fields are already prefilled):
class ScheduleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ScheduleForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
if instance is not None:
self.fields['selected_schedule'].choices = (
(instance.schedule1, instance.schedule1),
(instance.schedule2, instance.schedule2),
(instance.schedule3, instance.schedule3),
)
On your admin, simply state that you want to use that form:
class TheAdminInQuestion(admin.ModelAdmin):
form = ScheduleForm
Further note:
I'd recommend a different solution to your problem than storing the choices on the same model. For instance, you might have a model called ScheduleChoice, and there could be 3 records of that, etc. Or, you might calculate the value based on some other rules, and just don't store the choices at all. Also, I'd recommend using DateTimeField to store the dates. You can convert the date to any format you like (e.g. January 12th, 2011 at 3:35PM) and still store it as the same datetime object in the database.

Storing the edit history of a django model in another custom model

I have two models lets say:
class superfields(Model):
fieldA = models.FloatField()
fieldB = models.FloatField()
class Meta:
abstract = True
class my_model( superfields ):
def has_history( self ):
return self.my_model_history_set.count() > 0
class my_model_history( superfields ):
reason = models.TextField()
mymodel = models.ForeignKey( my_model )
'my_model' is populated with data (under fieldA and fieldB). Whenever someone edits 'my_model's fields and saves, I don't want to save the change in this model but want to store it as a new row with all values in 'my_model_history', in addition to a 'reason' field while 'my_model' data stays the same.
What is the best way to approach this scenario in terms of custom templates, custom views, model admins etc etc. Am I doing it correctly?
To give my question above some sense, in my project, the nature of data under 'my_model' is market prices and I need to maintain a history of all the market prices ever edited with a 'reason' for the edit.
Instead of editing an existing entry, why not use that entry as initial data for a form to create a new instance? The new object gets saved, the original stays the same...
My Solution:
yes. A simple and quick solution I am following is as follows:
I create three models similar to this:
class my_super_abstract_model(Model):
#All fields I need to keep a history for:
fieldA = models.FloatField()
fieldB = models.FloatField()
class Meta:
abstract = True
class my_model( my_super_abstract_model ):
def has_history( self ):
return self.my_model_history_set.count() > 0
class my_model_history( my_super_abstract_model ):
reason = models.TextField()
history_entry_for = models.ForeignKey( my_model )
I've setup a signal:
pre_save.connect( create_history,
sender = my_model_history )
and 'create history' to be called by the pre_save() signal before saving in my_model_history:
def create_history(sender, **kwargs):
#get variables passed by the pre-save signal:
history_model = kwargs['instance']
# Get main model object
main_model = history_model.history_entry_for
# swap all common fields between history edit and main model (except id)
main_model_fields = [f.name for f in main_model._meta.fields]
history_model_fields = [f.name for f in history_model._meta.fields]
field_index = list( [f for f in history_model_fields if f in main_model_fields and f != 'id' and f != 'created_date' ] )
#loop thru to swap values:
for field_name in field_index:
temp = getattr(main_model, field_name)
setattr( main_model, field_name, getattr( history_model, field_name ) )
setattr( history_model, field_name, temp)
# After the swap, save main model object here
main_model.save()
Whenever user clicks on a my_model row for editing, I use 'my_model_history' to generate my edit form and populate it with the values from the user selected row. (Have written a view and template to do that)
So the edit form will now have:
field A -populated with values from
my_model data row
field B -populated with values from
my_model data row
Reason -empty text box
history_entry_for -hidden from view
User can now edit fieldA/fieldB. Enter a reason. Press save to trigger the signal above.
Before saving,
Signal will swap the values between
the main model(old values) and
history model(New values)
Replace and save the main model row
(with the new values).
Insert and save a new row in the
history model (with the old values)
with a reason.
Hope it helps. Let me know if there are any further questions.
I found an explanation on keeping detailed edit histories in the book 'pro Django' page 264. After a read through I'll try an implementation of what I need. Will post my approach here when I'm done