Request changes/additions to data in another app - django

To make a long story short, I am very grateful for hints on how I can accomplish the following. I have an app A that I don't want to change. I have an app B that needs to select data from A or to request data to be added/changed if necessary. Think of B as an app to suggest data that should end in A only after review/approval. By itself, B is pretty useless. Furthermore, a significant amount of what B's users will enter needs to be rejected. That's why I want A to be protected so to say.
# in app A
class Some_A_Model(models.Model): #e.g., think artist
some_value = models.TextField()
# in app B
class MyCustomField(models.ForeignKey):
...
class Some_B_Model(models.Model): # e.g., think personal preference
best_A = MyCustomField('Some_A_Model')
worst_A = MyCustomField('Some_A_Model')
how_many_times_I_like_the one_better_than_the_other = models.FloatField()
class Mediator(models.Model):
# already exists: generic foreign key
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(
'content_type',
'object_id'
)
#does not yet exist or needs to be changed:
add_or_change = PickledObjectField()
Django should create a form for Some_B_Model where I can select instances of Some_A_Model for best_A and worst_A, respectively; if, however, my intended best_A is not yet in A's database, I want to be able to request this item to be added. And if I find worst_A is present but has a typo, I want to be able to request this item to be corrected. An editor should be required to review/edit the data entered in B and either reject or release all the associated changes to A's database as an atomic transaction. I don't want any garbage in A and refrain from adding some status field to track what is considered valid, requiring filtering all the time. If it's in A, it must be good.
I figured I need to define a MyCustomField, which could be a customized ForeignKey. In addition, I need some intermediate model ('mediator' maybe?) that MyCustomField would actually be pointing to and that can hold a (generic) ForeignKey to the item I selected, and a pickled instance of the item I would like to see added to A's database (e.g., a pickled, unsaved instance of Some_A_model), or both to request a change. Note that I consider using PickledObjectField from 'django-picklefield', but this is not a must.
As there is only some documentation on custom model fields but not on the further steps regarding form fields and widgets, it seems I have to dig through django's source to find out how to tie my intended functionality into its magic. That's where I am hoping for some comments and hints. Does my plan sound reasonable to you? Is this a known pattern, and if so, what is it called? Maybe someone has already done this or there is a plugin I could look into? What alternatives would you consider?
Many thanks in advance!
Best regards

Related

django model inheritance & FK, could not create unique index

I want to be able to have a foreign key to a parent class, thereby allowing queries of the children classes as well. All other solutions are nightmarish.
I have tried to make this (Destination is also a parent class that has a # of children classes I want to relate to):
class Destination(PolymorphicModel)
class Account(Destination)
class Organization(Destination)
class Person(Destination)
class Transaction(models.Model)
destination = models.ForeignKey(Destination, verbose_name="Destination", null=True, blank=True,
related_name="CompletedTransaction_Destination_FK")
I am referencing destination in other places as well.
This is the error message I get when I try to migrate:
psycopg2.IntegrityError: could not create unique index "baseapp_organization_organization_destination_ptr_id_key"
DETAIL: Key (organization_destination_ptr_id)=(1) is duplicated.
I would love it if I could make the destination class
abstract = True
but then I can't have a foreign key. I need to be able to choose all of those destinations, and they need to remain distinct, real models in the database.
I have also tried GenericRelations, but that proved to be a nightmare as I said earlier.
It feels like I could just get around this error somehow though, any help?
The solution, and I'm guessing it would apply to other errors where the index could not be created, was to wipe the database an delete all the migrations. It was a pain to be sure, but now I can do:
destinations = Destination.objects.all()
and it will give me all the objects, as according to django polymorphic

django and DB modeling: I think I've got Many to Many wrong

I have an object Option which is like a type, it only has a property: name (it could have more properties in the future).
Instances of Option have a unique name.
Many objects may have zero or more of Option instances.
For example:
class Consumer:
options = models.ManyToManyField(Option, blank=True, help_text="the options")
But then, in order to create the Option instances for Consumer's many-to-many options relationship, I need to create a new Option instance and add it to options.
This however "breaks" my uniqueness: Now I have two with the same name! And so forth for every instance of Option I create.for Many-to-Many links. Instead of the 4 I need, I have now 68 in my DB...
I believe I have fundamentally misunderstood Many-To-Many, and / or mis-designed this relationship...
Can anybody help?
EDIT: Here's how I set options in an example:
def enable_option(request, pk=0, option_pk=0, *args, **kwargs):
consumer = get_object_or_404(Consumer, pk=pk)
option = get_object_or_404(Option, pk=option_pk)
new_option = Option()
new_option.name = option.name // I know I am breaking my own rule...but when I read the consumer options, I need the exact same name! Still, I believe I am modeling wrong
new_option.save()
consumer.options.add(new_option)
consumer.save()
return HttpResponse()
I don't really understand why you create a new Option here. You get the existing one; you can just add that to the relationship:
consumer = get_object_or_404(Consumer, pk=pk)
option = get_object_or_404(Option, pk=option_pk)
consumer.options.add(option)
You don't even need to call save, as modifying an m2m does not change the instance itself.

In Django, how can I calculate or update certain model fields BEFORE any validation happens?

So I'm new to Django...
First some background on how we do things now. We have a custom php system but I am constructing an improved inventory management system in django using only the admin interface. We store part numbers, and it is essential that we do not store duplicates. Part numbers can sometimes be entered with hypens, periods, spaces, etc. We need to be sure that duplicate parts are not added no matter what kind of formatting is entered. With our existing non-django system, we use a regex to strip anything from the string that is not a-zA-Z0-9. The actual entered part number is persisted, and the cleaned number is persisted to the db as well. Then when someone is adding a new part or even searching for a part, this cleaned version of the part number helps to avoid this ambiguity. We do the same for the manufacturer name.
My way of emulating this in django was to add the part_number_clean field along with the part_number field to the model. Then I overrode the save method to calculate the clean part number like so (manufacturer as well):
def save(self, *args, **kwargs):
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
super(CatalogProduct, self).save(*args, **kwargs)
The problem is, I need to unique on a combination of part number and manufacturer:
class Meta:
unique_together = ('part_number_clean ', 'manufacturer_clean ')
When I try to save a duplicate record, I get a database integrity violation. So it seems like django is evaluating the unique fields before the save function is called (which makes sense). I just need to know how or which method I should override to calculate these fields BEFORE any validation.
Additionally, I am interested in adding a third field to the unique_together mix that may or may not be filled out. If it is not filled it will just have an empty default value. I hope this will not cause any issues.
It would also be great if when the user tabbed-out of the manufacturer and part number fields, and both were not empty, some js would see if that product exists already, and offer the user the option to click a button and be whisked away to that record, before they waste their time filling out the rest of the data only to find that it already exists. I'm guessing this lies way outside the realm of the admin interface without serious hacking. Is there any way to somehow integrate this with the admin interface? Its working great for me up till now...
I figured it out. I'm posting the answer for anyone else that is curious. This was actually very simple in the end to implement in the model. All one needs to do is implement (override?) the clean() method of the model. In the method, I calculate and set my special fields, then be sure to call self.validate_unique() after. Works like a charm! No need to raise any exceptions, the form will display the error on top perfectly. Doing this in the save method will not work, as the exception cannot be thrown by your code or django at that point. Here is the code:
class CatalogProduct(models.Model):
manufacturer = models.CharField(max_length=100)
manufacturer_clean = models.CharField('Manufacturer',max_length=100,blank=True,editable=False)
part_number = models.CharField(max_length=100)
part_number_clean = models.CharField('Part number',max_length=100,blank=True,editable=False)
def clean(self):
# Calculate manufacturer_clean and part_number_clean
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
self.validate_unique()
The model is only responsible for describing data and how that data should be represented between your Python and database environment. It's because of this atomic role that models don't care about validation and what you've just went in there and introduced it.
You need a model form. It can clean the manufacturer and part number and also ensure that uniqueness constraints are satisfied as part of the validation process.

Before syncdb, delete field from standard Django model

This is a follow-up question on Delete field from standard Django model. In short: a field can be dynamically deleted from a model that is already created, in this case the field User.email . So field email would be deleted from User without changing the code for User. See code below for example.
I can dynamically delete a a field from a model(1), but that happens when the server starts and is undone when it exists. Since syncdb doesn't require the server to be running, and generally seems to ignore the deletion code (somehow), this approach doesn't prevent the field from appearing in the database(2).
Is there a way to do delete the field from the model (without changing the file it's in, as it's a Django model), in a way that also makes it not appear in the database?
Thanks in advance!
Mark
EDIT: I problem is not that I am deleting the text "m = models.IntegerField()" from the model file and want the field removed from the database. The problem is that I am using the code below to remove a field from a model that has already been declared in another file. I do not think that creating a migration with South for every time I run syncdb is a solution(3).
Additional information:
1) Currently, the code is in models.py, but I suppose Where to put Django startup code? works.
2) I can delete it on post_syncdb signal with custom query, but I hope for something more elegant... Or elegant at all, more accurately.
3) If it even works at all, because obviously syncdb is still seeing the 'removed' field), so I think South would to as it's still somehow there.
This is the code (models.py):
class A(models.Model):
m = models.IntegerField()
for i, f in enumerate(A._meta.fields):
if f.name == 'm':
del A._meta.fields[i]
break
class B(A):
n = models.IntegerField()
for i, f in enumerate(B._meta.fields):
if f.name == 'm':
del B._meta.fields[i]
break
EDIT: I checked (with print) and the deletion code is executed on syncdb. It is executed before tables are created
django does a lot of meta class magic and i would guess that the meta class is responsible for defining the database table to back your model. Subsequently just deleting the field is not enough to alter the generated table.
as several people have pointed out, south is the way to deal with these problems.

Using aggregation in Django with

Following is my model:
class Story(models.Model):
author = models.ForeignKey(User, related_name='author_id', default=1)
source = models.ForeignKey(User, related_name='source_id', default=2)
User is the django provided model.
Now I need to find out number of articles authored and sourced by each user.
I thought of using story_set with user object:
res = User.objects.annotate(Count('story_set'))
However, there are two columns in story referencing User. Hence, Django won't know which one to use?
Can anyone help me on this?
story_set doesn't exist one way or another. That would've been the default related_name if you hadn't provided one. Since you did, you have to use those.
res = User.objects.annotate(Count('author_id'))
OR
res = User.objects.annotate(Count('source_id'))
So, that's how Django knows the difference.
FYI: if you had used the default (so you accessed stories via .story_set.all(), you don't use the "_set" part in queries. It would just be Count('story').
the reason django makes you specify a related_name is exactly for this reason. you want Count('author_id') instead of Count('story_set') (and you should probably give these better names, e.g. author_set)
res = User.objects.annotate(num_authored=Count('author_id')).annotate(num_sourced=Count('source_id'))
res[0].num_authored
res[0].num_sourced
If you want both the number of authored and number of sourced articles in one query. You might want more appropriate related names, like "authored" and "sourced".