django-nonrel: add element in LisftField of ForeignKeys - django

I'm trying to implement a list of foreignKeys in django-nonrel (I'm using mongo as db).
Here is the code:
# models.py
from django.db import models
from django_mongodb_engine.contrib import MongoDBManager
from djangotoolbox.fields import ListField
class FriendList(models.Model):
objects = MongoDBManager()
list = ListField(models.ForeignKey('AWUser'))
def add_friend(self, awuser):
# awuser must be an instance of AWUser - I removed tests for more clarity
self.list.append(awuser)
self.save()
class AWUser(models.Model):
objects = CustomUserManager()
user = EmbeddedModelField('User')
friends = EmbeddedModelField('FriendList')
The problem is that when I call user.friends.add_friend(user1), I have the error "AttributeError: 'str' object has no attribute '_meta'".
Breaking example (made using ./manage shell console):
$>user = AWUser.objects.all()[0]
$>user1 = AWUser.objects.all()[1]
$>user.friends.add_friend(user1)
#ask me if you need the complete error - I don't put it more more clarity
AttributeError: 'str' object has no attribute '_meta'
What I basically need is to create friend lists.
Please feel free to recommend a different implementation if you think mine is not good. :) I would love to have my implementation working though...
Also, I did not put all the variables of AWUser for more clarity but I can add them if necessary.
Related project dependencies:
django 1.3.1 (installed as django-nonrel)
django-mongodb-engine 0.4.0
djangotoolbox==0.9.2
List item
pymongo==2.1.1
Thanks for your help.
UPDATE:
I tried to change the code as said in the post "ListField with ForeignField in django-nonrel" but I still have the same error...

According to the Django MongoDB Engine documentation, it suggests to use EmbeddedModel from djangotoolbox:
from djangotoolbox.fields import ListField, EmbeddedModelField
class Post(models.Model):
...
comments = ListField(EmbeddedModelField('Comment'))
class Comment(models.Model):
text = models.TextField()
Edit: Forgot the link: http://django-mongodb-engine.readthedocs.org/en/latest/topics/embedded-models.html

I actually just figured out what was wrong.
It is apparently impossible to declare the foreign key class type as a string when in a Listfield. Weird...
If it happens to you, just do the following change:
list = ListField(models.ForeignKey('AWUser'))
becomes:
list = ListField(models.ForeignKey(AWUser))
If anyone as a good explanation of what is going on, I'd love to hear it :)

Related

Link column with a static text using django-tables2

I cannot find a replacement for the LinkColumn in the new versions of django-tables2. Author states that LinkColumn is deprecated and shouldn't be used. But the new linkify solution is poorly documented and doesn't have all the features of the old version. For example, I have this column:
edit = tables.LinkColumn(
'wagtailadmin_pages:edit', args=[A('page.pk')],
text='Edit'
)
It displays a link to the wagtail admin edit page called Edit. There's simply no way to achieve the same using linkify because linkify only works if you have valid accessor on the column. But accessor cannot return same static text for all rows (unless I modify the model to add a dummy property - but this particular model is in the 3rd party package and it would feel like a duct tape solution anyway).
In all other cases, column will not display a link. I've studied the source code and it seems that such case is simply not supported by the django-tables2 > 2.0.0.
Is there any clean and understandable way to construct a link column with a static link text using linkify?
Answering my own question. It seems that it is impossible to fully replace LinkColumn with the linkify feature. The following code solves my problem:
from django.urls import reverse
from django.utils.text import mark_safe
import django_tables2 as tables
from wagtail.core.models import PageRevision
class WagtailRevisionsTable(tables.Table):
title = tables.Column(
accessor='page.title',
linkify=lambda record: record.page.url,
verbose_name='Title'
)
edit = tables.Column(
accessor='page.pk'
)
class Meta:
model = PageRevision
fields = ('title', 'created_at', 'user', 'edit')
template_name = 'django_tables2/bootstrap-responsive.html'
def render_edit(self, value):
url = reverse('wagtailadmin_pages:edit', args=[value])
return mark_safe(f'Edit')
The code with the old LinkColumn was much more concise, I don't understand the reason for change and documentations really doesn't help. There's simply not enough information on linkify or render_col methods.
So I hope this answer will help some poor soul trying to port old code to the django-tables2 >= 2.0.

Indexing Taggit tags with Algolia for Django: '_TaggableManager' object has no attribute 'name'

I'm having some issues using the Algolia Django integration with one of my models which contains a TaggitManager() field. I'm currently being thrown back the following error when running this command:
$ python manage.py algolia_reindex
AttributeError: '_TaggableManager' object has no attribute 'name'
I've had a look at the Taggit documentation, but I'm just not sure exactly how I would marry the method outlined with the Algolia search index method.
index.py:
import django
django.setup()
from algoliasearch_django import AlgoliaIndex
class BlogPostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'blog_post_index'
models.py:
from taggit.managers import TaggableManager
class Post(models.Model):
...some model fields...
tags = TaggableManager()
To index the taggit tags with your Post fields, you will need to expose a callable that returns a Blog Post's tags as a list of strings.
The best option is to store them as _tags, which will let you filter on tags at query time.
Your PostIndex would look like this:
class PostIndex(AlgoliaIndex):
fields = ('title', '_tags')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
As for Post:
class Post(models.Model):
# ...some model fields...
tags = TaggableManager()
def _tags(self):
return [t.name for t in self.tags.all()]
Following these instructions, your records will be indexed with their respective tags:
You can check the taggit branch of our Django demo, which demonstrates these steps.
To answer my own question. I have now passed in both the model and the model index so Algolia now knows what to index and what not to index. Although I would like a method to allow Algolia to index taggit tags, alas, it is probably not possible.
My apps.py file:
import algoliasearch_django as algoliasearch
from django.apps import AppConfig
from .index import PostIndex
class BlogConfig(AppConfig):
name = 'blog'
def ready(self):
Post = self.get_model('Post')
algoliasearch.register(Post, PostIndex)
My index.py file:
from algoliasearch_django import AlgoliaIndex
class PostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
And that should pretty much work! Simple when you know how, or after trying about 10 different options!
So since nobody is answering I tell you how I solved this issue but I have to say that it is not a nice Way and not a "clean" Solution at all. So what I did is went into "taggit managers" in the site-packages (env->lib->python2.x/3.x-> site_packages->taggit->managers.py) In the managers.py file you will find at line 394 this beautiful piece of code:
def __get__(self, instance, model):
if instance is not None and instance.pk is None:
raise ValueError("%s objects need to have a primary key value "
"before you can access their tags." % model.__name__)
manager = self.manager(
through=self.through,
model=model,
instance=instance,
prefetch_cache_name=self.name, # this is the line I comment out when building the index,
name=self.name #this is the line I added and needs to be commented out after the index is build.
)
return manager
So what I do when I want to rebuild the search index is comment out (putting"#" infront of the line) prefetch_cache_name=self.name, and replace it with name=self.name. So building the index will work. After the Index is finished building, you have to bring everything back as it was before (switch the "#" to name=self.name again and leave prefetch_cache_name=self.name, visible again).
As already mentioned this is probably not the best way but I had the same pain and this is working for me. It takes one minute when you have the routine. Since I have to rebuild the Index maybe once every two weeks, that isn't such a deal for me but if you have to do it very often this might be annoying...
Anyway I hope that helps you.
It can help you if you using django==2+
The problem is in get_queryset() method of TaggableManager
Open file with it (my path was: Pipenv(project_name)/lib/site-packages/taggit/manager.py)
Find _TaggableManager class and change method name get_queryset to get_query_set
Done. I wish taggit's developers will fixed this in future updates

Django - Foreign key to another app with a complex name

I'm writing a Django Model that will link to another app Model. I know that we should link the ForeginKeys with "nameoftheapp.nameofthemodel" but I'm not successful doing it with this use case.
Here is my installed_apps:
INSTALLED_APPS = (
...
'signup',
'paypal.standard.ipn',
...
)
Basically I'm creating a model in the app "signup" and I need to do a foreignkey to "paypal.standard.ipn".
Here is my model:
class SignupPaymentPayPal(models.Model):
gettingstarted = models.ForeignKey('GettingStarted')
paypalipn = models.ForeignKey('paypal.standard.ipn.PayPalIPN')
The model I need to link is this one, https://github.com/spookylukey/django-paypal/blob/master/paypal/standard/ipn/models.py
When I try to do a shemamigration I got this:
$ python manage.py schemamigration signup --auto
Here is the error I got:
CommandError: One or more models did not validate:
signup.signuppaymentpaypal: 'paypalipn' has a relation with model paypal.standard.ipn.PayPalIPN, which has either not been installed or is abstract.
Any clues on what I'm doing wrong?
Best Regards,
from paypal.standard.ipn.models import PayPalIPN
class SignupPaymentPayPal(models.Model):
gettingstarted = models.ForeignKey('GettingStarted')
paypalipn = models.ForeignKey(PayPalIPN)
André solution works, and in fact I'd recommend using the actual model instead of a string whenever possible, to avoid any unexpected errors when the string can't be resolved to a model, but here's an explanation why your previous method didn't work:
Django has a model cache that keeps track of all Model subclasses that are created. Each model is uniquely identified by an appname and modelname, but not by a fully qualified import path.
Let's say I have two models, one is myapp.reviews.Review, the other is myapp.jobs.reviews.Review. If myapp.reviews.Review comes first in my INSTALLED_APPS, both classes will actually be the myapp.reviews.Review model:
>>> from myapp.jobs.reviews import Review
>>> Review
<class 'myapp.reviews.Review'>
To specify a ForeignKey using a string instead of a class (e.g. to avoid circular imports), you need to follow the '<appname>.<modelname>' format, i.e.:
class SignupPaymentPayPal(models.Model):
paypalipn = models.ForeignKey('ipn.PayPalIPN')

Using the Django ManyToMany relationship example. Need to initialization first?

Just finished the Django tutorial. Starting my own project. Using the ManyToMany Relationship example here. I added in the following controls in my admin.py:
class ArticleInline(admin.TabularInline):
model = Article
extra = 3
class PublicationAdmin(admin.ModelAdmin):
inlines = [ArticleInline]
admin.site.register(Publication, PublicationAdmin)
But when I try to add a publication in the admin page, it says:
<class 'polls.models.Article'> has no ForeignKey to <class 'polls.models.Publication'>
Do I have to initialize an article before I can create a publication? Does creating a publication make Django look for an article?
Thanks for the help. Just starting out. It's pretty overwhelming, but very exciting.
If you're using a many to many relationship, you need to access the actual through model.
I'm assuming you have something like:
class Publication(models.Model):
[...] # your other stuff
articles = models.ManyToManyField(Article)
Assuming that construction, you access the through model using the attribute spelled similarly:
class ArticleInline(admin.TabularInline):
model = Publication.articles.through
[...] # whatever else you need
Hope that helps!

In Django/South HOWTO create an instance of a model from a different app during DataMigration

I need to perform a datamigration of a model Answer in app Question. In that script there is a dependency such that I need to create an instance of a model Chapter which is in the app Journal. So, I coded it as follows:
def forwards(self, orm):
for answer_object in orm.Answer.objects.all():
#This Works.
blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
blog.save()
#This DOES NOT work
chapter, is_created = orm['journal.Chapter'].objects.get_or_create(content_object=blog)
chapter.save()
#cleanup task, not relevant to this question below
answer_object.chapter_ptr = chapter
answer_object.save()
But as expected this throws an error on " orm['journal.Chapter'].objects.get_or_create(content_object=blog)" saying that
django.core.exceptions.FieldError: Cannot resolve keyword 'content_object' into field.
This is presumably due to content_object being a GenericForeignKey so some operations are not allowed. But I also tried other alternatives for creating the "chapter" object like,
chapter = orm['journal.Chapter'](content_object=blog)
ERROR > TypeError: 'content_object' is an invalid keyword argument for this function
and
chapter = orm.journal.Chapter(content_object=blog)
ERROR > AttributeError: The model 'journal' from the app 'questions' is not available in this migration. (Did you use orm.ModelName, not orm['app.ModelName']?)
So where am I going wrong? Any pointers appreciated. Thanks.
UPDATE
So since my earlier approach was failing I tried a new tack. The model whose instantiation was failing in my code above i.e. Chapter in the Journal app, I decided to create a datamigration for that instead. I also made sure to --freeze the models I am referring to in the forwards definition. Now this should have been straight forward, I would think. I have my forward code as follows -
def forwards(self, orm):
for answer_object in orm['questions.Answer'].objects.all():
#Works, AGAIN!
blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
blog.save()
# DOES NOT WORK, AGAIN!
chapter = orm.Chapter(rank=1, content_object=blog)
chapter.save()
I would have thought that now since I am creating instance of a model (Chapter) which exists in the subject app (Journal) everything should have worked out. But i am getting the same error.
TypeError: 'content_object' is an invalid keyword argument for this function
It fails at the same point, namely, "content_object". I will post below the model definition if that might help.
class Chapter(models.Model):
rank = models.IntegerField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
UPDATE 2
Wanted to add that all the models being touched in these forwards methods, namely - blog, chapter, questions; are fully defined in the 00n_*.py files created by South's schemamigration.
After getting help from Rob and folks on the South & Django user groups I was able to resolve this issue. Below is the definition of my forwards datamigration script.
def forwards(self, orm):
for answer_object in orm['questions.Answer'].objects.all():
blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
blog.save()
#I have to manually lookup the content_type ans set it in the chapter creation.
ct = orm['contenttypes.ContentType'].objects.get(app_label="blog", model="post")
chapter = orm.Chapter(rank=1, content_type=ct, object_id=blog.id)
chapter.save()
This has sort of been answered before here Django South: Creating schemamigration for more than one app
Basically south doesn't currently support multiple applications.
If you don't want to entangle your applications even more than they are, I would use raw SQL in a db.execute as a quick fix.
It looks like blog and journal are very inter-related. Are you sure you want them in separate applications?
In order to create a migration via manage.py, pass --freeze=other_app as arguments to add the models definitions of this other_app to the migration itself.
This is a comment on Chantz's answer, which worked great for me (Don't have the rep to post this as a comment)
To save some others some time, you also need to freeze the contenttype app when you create your migration:
python manage.py datamigration yourapp migrationname --freeze contenttypes