Django Set Default as Subset of ManyToMany Model Field - django

class UserProfile(models.Model):
project_assignments = models.ManyToManyField('drawingManager.Project', default=SetDefaultProject(self,default_assignment))
user = models.OneToOneField(User)
default_project
def SetDefaultProject(self,default_assignment):
if default_assignment:
self.default_project = default_assignment
else:
self.default_project = #somehow choose newest project
def __unicode__(self):
admin_reference = self.user.first_name + self.user.last_name
return admin_reference
I'm trying to achieve the following behavior. When a user is added at least one project assignment is set at the default. And they can later, through an options interface set their default to any of the subset of project_assignments. But it's not clear to me when you can use Foreign Key and Many to Many Fields as just python code and when you can't.

If I understand you correctly, you're not understanding that ForeignKeys and ManyToManyFields return different things.
A ForeignKey is a one-to-many relationship, with the 'one' on the side that it's pointing to. That means that if you defined default_project as a ForeignKey, self.default_project returns a single Project instance which you can use and assign as any other instance.
However, a ManyToManyField - as the name implies - has "many" relationships on both sides. So self.project_assignments doesn't return a single instance, it returns a QuerySet, which is the way Django handles lists of instances retrieved from the database. So you can use add and remove to manipulate that list, or slice it to get a single instance.
For example, if you wanted to set the default_project FK to the first project in the project assignments list, you would do:
self.default_project = self.project_assignments.all()[0]
(although in real code you would have to guard against the probability that there are no assignments, so that would raise an IndexError).

I'm not sure I undertsand what you mean by "it's not clear to me when you can use Foreign Key and Many to Many Fields as just python code", but your pseudo code would work with the following changes:
def SetDefaultProject(self,default_assignment):
if default_assignment:
self.project_assignments.add(default_assignment)
else:
self.project_assignments.add(self.project_assignments.latest('id'))
# note: i don't know what your intent is, so please adapt to your needs
# the key here is `MyModel.objects.latest('id')` to retrieve the latest object
# according to the field specified. 'id', 'date_created', etc.
PS: it's recommended to use lowercase names & underscores for method names (to not confuse them with ClassNameFormatRecommendations)
http://www.python.org/dev/peps/pep-0008/

Related

Django - copy and insert queryset clone using bulk_create

My goal is to create a clone of a queryset and then insert it into the database.
Following the suggestions of this post, I have the following code:
qs_new = copy.copy(qs)
MyModel.objects.bulk_create(qs_new)
However, with this code I run into duplicate primary key error. As for now, I only can come up with the following work-around:
qs_new = copy.copy(qs)
for x in qs_new:
x.id = None
MyModel.objects.bulk_create(qs_new)
Question: Can I implement this code snippet without going through loop ?
Can't think of a way without loop, but just a suggestion:
# add all fields here except 'id'
qs = qs.values('field1', 'field2', 'field3')
new_qs = [MyModel(**i) for i in qs]
MyModel.objects.bulk_create(new_qs)
Note that bulk_create behaves differently depending on the underlying database. With Postgres you get the new primary keys set:
Support for setting primary keys on objects created using
bulk_create() when using PostgreSQL was added.
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create
You should, however make sure that the objects you are creating either have no primary keys or only keys that are not taken yet. In the latter case you should run the code that sets the PKs as well as the bulk_create inside transaction.atomic().
Fetching the values explicitly as suggested by Shang Wang might be faster because only the given values are retrieved from the DB instead of fetching everything. If you have foreign key relations or m2m relations you might want to avoid simply throwing the complex instances into bulk_create but instead explicitly naming all attributes that are required when constructing a new MyModel instance.
Here an example:
class MyModel(Model):
name = TextField(...)
related = ForeignKeyField(...)
my_m2m = ManyToManyField(...)
In case of MyModel above, you would want to preserve the ForeignKey relations by specifying related_id and the PK of the related object in the constructor of MyModel, avoiding specifying related.
With m2m relations, you might end up skipping bulk_create altogether because you need each specific new PK, the corresponding original PK (from the instance that was copied) and the m2m relations of that original instance. Then you would have to create new m2m relations with the new PK and these mappings.
# add all fields here except 'id'
qs = qs.values('name', 'related_id')
MyModel.objects.bulk_create([MyModel(**i) for i in qs])
Note for completeness:
If you have overriden save() on your model (or if you are inheriting from 3rd party with custom save methods), it won't be executed and neither will any post_save handlers (yours or 3rd party).
I tried and you need a loop to set the id to None, then it works. so finally it may be like this:
qs_new = copy.copy(qs)
for q in qs_new:
q.id = None
# also, you can set other fields if you need
MyModel.objects.bulk_create(qs_new)
This works for me.

Concise way of getting or creating an object with given field values

Suppose I have:
from django.db import models
class MyContentClass(models.Model):
content = models.TextField()
another_field = models.TextField()
x = MyContentClass(content="Hello, world!", another_field="More Info")
Is there a more concise way to perform the following logic?
existing = MyContentClass.objects.filter(content=x.content, another_field=x.another_field)
if existing:
x = existing[0]
else:
x.save()
# x now points to an object which is saved to the DB,
# either one we've just saved there or one that already existed
# with the same field values we're interested in.
Specifically:
Is there a way to query for both (all) fields without specifying
each one separately?
Is there a better idiom for either getting the old object or saving the new one? Something like get_or_create, but which accepts an object as a parameter?
Assume the code which does the saving is separate from the code which generates the initial MyContentClass instance which we need to compare to. This is typical of a case where you have a function which returns a model object without also saving it.
You could convert x to a dictionary with
x_data = x.__dict__
Then that could be passed into the object's get_or_create method.
MyContentClass.objects.get_or_create(**x_data)
The problem with this is that there are a few fields that will cause this to error out (eg the unique ID, or the _state Django modelstate field). However, if you pop() those out of the dictionary beforehand, then you'd probably be good to go :)
cleaned_dict = remove_unneeded_fields(x_data)
MyContentClass.objects.get_or_create(**cleaned_dict)
def remove_unneeded_fields(x_data):
unneeded_fields = [
'_state',
'id',
# Whatever other fields you don't want the new obj to have
# eg any field marked as 'unique'
]
for field in unneeded_fields:
del x_data[field]
return x_data
EDIT
To avoid issues associated with having to maintain a whitelist/blacklist of fields you, could do something like this:
def remove_unneeded_fields(x_data, MyObjModel):
cleaned_data = {}
for field in MyObjModel._meta.fields:
if not field.unique:
cleaned_data[field.name] = x_data[field.name]
return cleaned_Data
There would probably have to be more validation than simply checking that the field is not unique, but this might offer some flexibility when it comes to minor model field changes.
I would suggest to create a custom manager for those models and add the functions you want to do with the models (like a custom get_or_create function).
https://docs.djangoproject.com/en/1.10/topics/db/managers/#custom-managers
This would be the cleanest way and involves no hacking. :)
You can create specific managers for specific models or create a superclass with functions you want for all models.
If you just want to add a second manager with a different name, beware that it will become the default manager if you don't set the objects manager first (https://docs.djangoproject.com/en/1.10/topics/db/managers/#default-managers)

Add ModelForm Fields as Attribute to Object

I have a ModelForm for my Risk set up as:
class RiskForm(forms.ModelForm):
class Meta:
model = Risk
fields = '__all__'
def __init__(self, *args, **kwargs):
progid = kwargs.pop('progid')
super(RiskForm, self).__init__(*args,**kwargs)
dict_of_fields = {}
all_char = Program.objects.get(id=progid).char_set.all()
for char in all_char:
c = []
for cat in char.cat_set.all():
c.append( (cat.label, cat.label) )
dict_of_fields[char.label] = c
self.fields[char.label] = forms.ChoiceField(c)
Where the Risk Object is defined as:
class Risk(models.Model):
program = models.ForeignKey(Program, on_delete=models.CASCADE)
label = models.CharField(max_length=200, default='')
def __str__(self):
return self.label
However, I want to store the extra fields that I have created into my database under my Risk object.
As I have it now, it only stores the two attributes 'program' and 'label'. However, I also want to store the answers to the characteristics into my database for later usage.
For more information about how I've set things up: Django Form Based on Variable Attributes
And a print screen of my ModelForm: https://gyazo.com/89c9833613dbcc7e8d27cc23a3abaf72
Is it possible to store all 6 answers under my Risk Object in my database? If so, how do I do that?
A form in Django is a user interface object: it gives the user a set of fields to type answers into, checks that the data which they have supplied is valid, and converts it from text to the desired Python data-type (int, date, etc.). It does not have to relate to any particular model, and often doesn't. For example, an online shop is likely to have purchase selection forms which add data concerning possible orders into the user's session, rather than immediately performing any sort of update on a Product or Stock object. That happens later, at checkout.
A ModelForm assumes there is a Model to Form relationship. It is typically the right way to go when there is a simple relationship between the user's answers and a single model instance.
If you have a situation where the user's answers direct the creation of multiple database objects with a less direct relationship to any particular object, you probably don't want a ModelForm, just a Form. Also probably not a model-based view, but a function-based view. You can then do anything you need to between the view picking up the parameters from the URL parser, and displaying the form to the user. Likewise, anything between the view determining that the user's POST data is valid and telling the user whether his submitted request succeeded (or not, and why).
In this case I'm not clear how you want to store all six answers. If there's a predetermined fairly small set of answers you could have a single object with six? ten? possible sets of fields which are nullable to indicate that this object doesn't have that entity. Or, probably better, you could create a set of Answer objects each of which has a Foreign Key relationship to the Risk object, and later refer to Risk.answer_set (all the Answer objects which have a foreign key relationship to your risk object). This is open-ended, a Risk object can have anything from zero to bignum associated Answers.

Django multiple Many2Many relationships

I have created a model where I define a Place, which has several properties (M2M) and each property has several choices. A user is able to vote for one or more of the choices, therefore I define a M2M relationship from the choices to a user. However I do not achieve the required functionality, as the user is connected only to the choices and not to the specific place that has the choices. my model is as follows:
class Place(models.Model):
StoreName=models.CharField(max_length=200)
Pic=models.ImageField(upload_to="media",blank=True,null=True)
Address=models.CharField(max_length=200)
def __unicode__(self):
return self.StoreName
class TypeProperty(models.Model):
Place = models.ManyToManyField(Place)
TypePropertyName=models.CharField(max_length=42)
def __unicode__(self):
return self.TypePropertyName
class TypeChoices(models.Model):
TypeProperty = models.ForeignKey(TypeProperty)
TypeChoiceName=models.CharField(max_length=42)
UserVoted=models.ManyToManyField(User,blank=True,null=True)
How can I achieve the functionality
A User has voted for the Choice that for the specific Place
From User you can get TypeChoices:
user_instance.typechoices_set.all()
Then, you can loop through each TypeChoice and access the Place queryset through the TypeProperty attribute:
typechoice_instance.TypeProperty.Place.all()
Then you would need to loop through each Place to do whatever.
Now, looking at that, it should be immediately apparent that there's some serious flaws here. The code makes no sense, and it's not readable. This is why coding conventions exist. Please give the Python Style Guide (PEP8) a thorough read.
In particular to your code here, attributes on a class should be all lower-case and words in the attribute's name should be separated by underscores, i.e. store_name NOT StoreName.
Then, attribute names should parallel what they represent: Place makes no sense for a field that will return multiple places. Name it places to indicate that it returns multiple items (and in particular returns a queryset rather than a model instance).
UPDATE
If you want that, then you need to work backwards and select the TypeChoices:
TypeChoices.objects.filter(UserVoted=some_user, TypeProperty__Place=some_place)

django unique field

is there another REGEX way (or another way) to ensure that a model class field would be unique? (it is not a key, or at least not declared as a key, is shoulb be a simple CharField)
Thanks
The normal way to make a single field unique is to use the unique argument to the field constructor.
If you need to make this unique on more than one field, have a look at:
unique-together
There are two ways of doing so.
The first is to mark the entire column as unique. For example:
product_name = models.Charfield(max_length=10, unique=True)
This method is good when you want your entire column to be inherently unique regardless of the situation. This can be used for username, id, key etc.
However, if the column cannot be inherently unique but it has to be unique in relation to others, you have to use the manual way.
from django.core.exceptions import ObjectDoesNotExist
try:
n = WishList.objects.get(user=sample_user, product=sample_product)
# already exists
return False
except ObjectDoesNotExist:
# does not exist
wish_list = WishList(user=sample_user, product=sample_product)
wish_list.save()
return True
Take this as an example. You have a wish list which none of the items can be unique. A single user can have many products and a single product can be in the wish list of many users. However, a single user cannot add one particular product to his or her wish list more than once. And this is where unique=True cannot be used and we have to use try and except