Escape backslashes in Doctrine Migrations - doctrine-orm

I have a table called Entity. It has a field with the namespace of an entity like this:
Old\App\Email which I'm trying to rename to New\App\Email.
I got this working SQL query to rename all rows in the MySQL Console. It needs 4 \ in the condition and 2 \ in REPLACE:
UPDATE Entity SET entity_name = REPLACE(entity_name, 'Old\\App', 'New\\App') WHERE entity_name LIKE 'Old\\\\App%';
It was already hard to get it working correctly escaped, but now when trying to add it to a Doctrine Migration.
$this->addSql('update log set entity_name = replace(entity_name, \'Old\', \'New\') where entity_name like \'Old\App%\'');
It's executed without making changes. I assume it is not escaping properly.
I have trying all combination of escaping from on to 4 backslashes. And even tried the different combination of quote escaping hoping that this would yield the expected escaping but no row is being changed at all:
$this->addSql('update Entity set entity_name = replace(entity_name, \'Old\', \'New1\') where entity_name like \'Old\App%\'');
$this->addSql('update Entity set entity_name = replace(entity_name, \'Old\', \'New2\') where entity_name like \'Old\\App%\'');
$this->addSql('update Entity set entity_name = replace(entity_name, \'Old\', \'New3\') where entity_name like \'Old\\\App%\'');
$this->addSql('update Entity set entity_name = replace(entity_name, \'Old\', \'New4\') where entity_name like \'Old\\\\App%\'');
$this->addSql('update Entity set entity_name = replace(entity_name, "Old", "New5") where entity_name like "Old\App%"');
$this->addSql('update Entity set entity_name = replace(entity_name, "Old", "New6") where entity_name like "Old\\App%"');
$this->addSql('update Entity set entity_name = replace(entity_name, "Old", "New7") where entity_name like "Old\\\App%"');
$this->addSql('update Entity set entity_name = replace(entity_name, "Old", "New8") where entity_name like "Old\\\\App%"');
Any help from fellow programmers would be appreciated.
Doctrine version is 2.7

It's not pretty but it takes 7 or 8 \ in the where clause and 4 in the replace function outside wrapped in double quotes and the search strings with single quotes:
$this->addSql("update Entity set entity_name = replace(entity_name, 'Old\\\\App', 'New\\\\App') where entity_name like 'Old\\\\\\\\App%'");

Related

Dynamically Build Q Filter in Django, with AND and OR Operators?

I have a database table of articles which includes a field link, which is the link to the article.
I'd like to filter for articles in a given date range, that come from domains in a given list.
I've got a string array like this:
domain_names = [
'domain_name_1',
'domain_name_2',
'domain_name_3',
'domain_name_4',
]
...and I have a filter like this which works:
data = (myArticles.objects
.filter(Q(creation_datetime__range=[from_date, to_date]) & (Q(link__contains=domain_names[0]) | Q(link__contains=domain_names[1]) | Q(link__contains=domain_names[2]) | Q(link__contains=domain_names[3])))
)
I'd like to dynamically build the filter, so that if an object is added to or removed from the domain_names list, the filter will dynamically update.
I've tried this:
q_object = Q()
for name in domain_names:
q_object.add(Q(link__contains=name), Q.OR)
q_object = q_object.add(Q(creation_datetime__range=[from_date, to_date]), Q.AND)
...but I'm getting back all the objects in the date range, and the filter that's supposed to be filtering on domain_names isn't doing anything yet.
What's the correct way to dynamically build a Q filter in this case?
You can work with:
q_object = Q(
*[Q(link__contains=name) for name in domain_names],
_connector=Q.OR
)
and then retrieve data with:
data = myArticles.objects.filter(
q_object,
creation_datetime__range=(from_date, to_date)
)

rails_admin get the current entity and display custom enum

I'm trying to customize an enum field with something else than only the name.
For example my entity record from database have columns as: name, postal_code, id etc ..
and I would like to have something like this in the dropdown "#{name} #{postal_code}, #{department}
I'm doing this:
field :city, :enum do
enum do
# Here I would like to get the entity from DB in order to have all
# columns to something similar as =>
entity.collect.each { |c| "#{c.name} (#{c.postal_code}), #
{c.department.name}"}
end
end
but I don't know how to get the active records (entity in my example) of the actual value of City entity.
How can I do this?
note: that department belongs to another model who is associated to City
Considering the OP comments i'm going to asume that the model in question has this association defined like this:
belongs_to :city
And the city field itself something like this
rails_admin do
edit do
field :city
end
end
Because this way rails admin will render a select drop down that will allow you to search the cities without loading them all.
After that on the City model you can define the title method, quoting the docs
By default it tries to call "name" or "title" methods on the record in question. If the object responds to neither, then the label will be constructed from the model's classname appended with its database identifier. You can add label methods (or replace the default [:name, :title]) with:
RailsAdmin.config {|c| c.label_methods << :rails_admin_title }
The title method can then be defined
class City < ApplicationRecord
def rails_admin_title
"#{self.name} (#{self.postal_code}), #{self.department.name}"
end
end
In a related issue, you will probably want to configure how rails admin searches for the city, i'll just link to the docs.
You can do it, but you need to define a name and a (stable)value for each select drop down, a simple hash out to do it like this:
field :city, :enum do
enum do
entity = bindings[:object]
City.all.map do |c|
{c.id => "#{c.name} (#{c.postal_code}), #{c.department.name}"}
end
end
end
Im assuming each city has an id, but you can use any value you want to store on that field on your DB.
So your users would see the nice formatted string, but rails admin would post the form with the city id on the city field.

Advanced Django ORM query with operator.or_ fails with None Values

I'm trying to query the database and exclude some rows that contain one of some certain stings.
My simplified model looks like this:
class Message(models.model)
text = models.TextField(blank=True, null=True)
My query looks like follows:
import operator
ignored_patterns = ['<ignore>', ]
messages = Message.objects.exclude(
reduce(operator.or_, (Q(text__contains=pattern) for pattern
in ignored_patterns))
)
The problem i have is, that somehow Messages that have self.text = Noneare excluded too.
I'm thankful for every hint.
You can use and condition in exclude:
exclude = reduce(operator.or_, (Q(text__contains=pattern) for pattern
in ignored_patterns))
nulltext = Q(text__isnull = False)
messages = Message.objects.exclude( nulltext & exclude )
Also, read about use of null in strings:
Avoid using null on string-based fields such as CharField and
TextField because empty string values will always be stored as empty
strings, not as NULL.

app-engine ndb delete data

I'm new to app-engine [Python 2.7]
I would like to delete elements from my ndb (currently I don't care if it is one by one or all at once since none is working for me).
Version 1 based on this Q:
ps_ancestors = req_query.fetch()
for ps_ancestor in ps_ancestors:
self.response.write(ps_ancestor.key)
ps_ancestor.key.delete()
It continues to print the same data without actually deleting anything
Version 2:
[myId currently have only the values 1,2,3]
ndb.Key(myId, 1).delete()
ndb.Key(myId, 2).delete()
ndb.Key(myId, 3).delete()
The model:
class tmpReport (ndb.Model):
myId = ndb.IntegerProperty()
hisId = ndb.IntegerProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
What am I missing?
k = users.query(users.name == 'abhinav')
for l in k.fetch(limit = 1):
l.key.delete()
First of all, you should not define your Entity key as an IntegerProperty. Take a look at this documentation: NDB Entities and Keys
In order to delete an entity from datastore you should first retrieve it by using a query or by its ID. I recommend you to use a "keyname" when creating your entities (to use it as your custom ID):
# Model declaration
class tmpReport (ndb.Model):
hisId = ndb.IntegerProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
# Store the entity
report = tmpReport(id=1, hisId=5)
report.put()
Then to retrieve and delete the previous entity use:
# Retrieve entity
report = ndb.Key("tmpReport", 1).get()
# Delete the entity
report.key.delete()
Hope it helps.
query1 = tmpReport.query()
query2 = query1.filter(tmpReport.myId == int(myId))
#Add other filters as necessary
query3 = query2.fetch()
query3[0].key.delete()
Removes the first entity(element) returned assuming myID is unique so there is only one element in the list.

Retrieving untagged objects with django-tagging

What I'm looking for is a QuerySet containing any objects not tagged.
The solution I've come up with so far looks overly complicated to me:
# Get all tags for model
tags = Location.tags.all().order_by('name')
# Get a list of tagged location id's
tag_list = tags.values_list('name', flat=True)
tag_names = ', '.join(tag_list)
tagged_locations = Location.tagged.with_any(tag_names) \
.values_list('id', flat=True)
untagged_locations = []
for location in Location.objects.all():
if location.id not in tagged_locations:
untagged_locations.append(location)
Any ideas for improvement? Thanks!
There is some good information in this post, so I don't feel that it should be deleted, but there is a much, much simpler solution
I took a quick peek at the source code for django-tagging. It looks like they use the ContentType framework and generic relations to pull it off.
Because of this, you should be able to create a generic reverse relation on your Location class to get easy access to the TaggedItem objects for a given location, if you haven't already done so:
from django.contrib.contenttypes import generic
from tagging.models import TaggedItem
class Location(models.Model):
...
tagged_items = generic.GenericRelation(TaggedItem,
object_id_field="object_id",
content_type_field="content_type")
...
Clarification
My original answer suggested to do this:
untagged_locs = Location.objects.filter(tagged_items__isnull=True)
Although this would work for a 'normal join', this actually doesn't work here because the content type framework throws an additional check on content_type_id into the SQL for isnull:
SELECT [snip] FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
WHERE (`tagging_taggeditem`.`id` IS NULL
AND `tagging_taggeditem`.`content_type_id` = 4 )
You can hack-around it by reversing it like this:
untagged_locs = Location.objects.exclude(tagged_items__isnull=False)
But that doesn't quite feel right.
I also proposed this, but it was pointed out that annotations don't work as expected with the content types framework.
from django.db.models import Count
untagged_locs = Location.objects.annotate(
num_tags=Count('tagged_items')).filter(num_tags=0)
The above code works for me in my limited test case, but it could be buggy if you have other 'taggable' objects in your model. The reason being that it doesn't check the content_type_id as outlined in the ticket. It generated the following SQL:
SELECT [snip], COUNT(`tagging_taggeditem`.`id`) AS `num_tags`
FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
GROUP BY `sotest_location`.`id` HAVING COUNT(`tagging_taggeditem`.`id`) = 0
ORDER BY NULL
If Location is your only taggable object, then the above would work.
Proposed Workaround
Short of getting the annotation mechanism to work, here's what I would do in the meantime:
untagged_locs_e = Location.objects.extra(
where=["""NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)"""]
)
This adds an additional WHERE clause to the SQL:
SELECT [snip] FROM `myapp_location`
WHERE NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)
It joins to the django_content_type table to ensure that you're looking at the appropriate
content type for your model in the case where you have more than one taggable model type.
Change myapp_location.id to match your table name. There's probably a way to avoid hard-coding the table names, but you can figure that out if it's important to you.
Adjust accordingly if you're not using MySQL.
Try this:
[location for location in Location.objects.all() if location.tags.count() == 0]
Assuming your Location class uses the tagging.fields.TagField utility.
from tagging.fields import TagField
class Location(models.Model):
tags = TagField()
You can just do this:
Location.objects.filter(tags='')