Viewed   210 times

Let's suppose I retrieve an entity $e and modify its state with setters:

$e->setFoo('a');
$e->setBar('b');

Is there any possibility to retrieve an array of fields that have been changed?

In case of my example I'd like to retrieve foo => a, bar => b as a result

PS: yes, I know I can modify all the accessors and implement this feature manually, but I'm looking for some handy way of doing this

 Answers

5

You can use DoctrineORMEntityManager#getUnitOfWork to get a DoctrineORMUnitOfWork.

Then just trigger changeset computation (works only on managed entities) via DoctrineORMUnitOfWork#computeChangeSets().

You can use also similar methods like DoctrineORMUnitOfWork#recomputeSingleEntityChangeSet(DoctrineORMClassMetadata $meta, $entity) if you know exactly what you want to check without iterating over the entire object graph.

After that you can use DoctrineORMUnitOfWork#getEntityChangeSet($entity) to retrieve all changes to your object.

Putting it together:

$entity = $em->find('MyEntity', 1);
$entity->setTitle('Changed Title!');
$uow = $em->getUnitOfWork();
$uow->computeChangeSets(); // do not compute changes if inside a listener
$changeset = $uow->getEntityChangeSet($entity);

Note. If trying to get the updated fields inside a preUpdate listener, don't recompute change set, as it has already been done. Simply call the getEntityChangeSet to get all of the changes made to the entity.

Warning: As explained in the comments, this solution should not be used outside of Doctrine event listeners. This will break Doctrine's behavior.

Friday, October 28, 2022
1

Looks like you forgot the @ in your annotations :

/**
 *@ORMColumn(type="integer", nullable=true)
 */
protected $experience;

/**
 *@ORMColumn(type="integer", nullable=true)
 */
protected $cr;

/**
 *@ORMColumn(type="integer", nullable=true)
 */
protected $proficiencybonus;
Wednesday, November 2, 2022
 
cjk
 
cjk
3

Well there it is: it's not possible.

In the last year I've been working on an alternative ACL system that would allow to filter directly in database queries.

My company recently agreed to open source it, so here it is: http://myclabs.github.io/ACL/

Saturday, November 5, 2022
 
cy.
 
cy.
4

Short of iterating through the iterable and counting the number of iterations, no. That's what makes it an iterable and not a list. This isn't really even a python-specific problem. Look at the classic linked-list data structure. Finding the length is an O(n) operation that involves iterating the whole list to find the number of elements.

As mcrute mentioned above, you can probably reduce your function to:

def count_iterable(i):
    return sum(1 for e in i)

Of course, if you're defining your own iterable object you can always implement __len__ yourself and keep an element count somewhere.

Thursday, October 13, 2022
5

So, all my searching didn't yield a short solution. The answer to the question seems to be: no, there is no buit-in way to get the current date (or datetime) as a variable in the template.

In case others are searching for this topic, I'll try to give a summary of the possible workarounds that I can up with and that were suggested by other users.


  1. I could pass a context variable to the template from my view. In class-based views that could look like this (it is even an example in the docs):

    # file 'my_app/views.py'
    from django.utils import timezone as tz
    from django.views.generic import ListView
    
    class MyView(ListView)
        ...
    
        def get_context_data(self, **kwargs):
            ctx = super().get_context_data(**kwargs)
    
            now = tz.now()
            ctx['now'] = now
            ctx['today'] = tz.localtime(now).date()
    
            return ctx
    
  2. I could create a custom context processor that loads that variable to every template. In class-based views that could look like this:

    # file 'context_processors.py'
    from django.utils import timezone as tz
    
    def now_and_today(request):
        now = tz.now()
        return {
            'now': now,
            'today': tz.localtime(now).date(),
        }
    
    # file 'settings.py'
    ...
    TEMPLATES = [
        {
            ...
            'OPTIONS': {
                'context_processors': [
                    ...
                    'context_processors.today_and_now',
                ],
            },
        },
    ]
    ...
    
  3. I could create a custom template tag, like this:

    # file 'my_app/custom_template_tags/custom_time_tags.py'
    from django.utils import timezone as tz
    from django import template
    register = template.Library()
    
    @register.simple_tag
    def get_now(request):
        return tz.now()
    
    @register.simple_tag
    def get_today(request):
        return tz.localtime(tz.now()).date()
    

    To be used like this:

    {% load 'custom_time_tags' %}
    
    {% get_today as today %}
    {% for per in person_list %}
        {% if per.brith_date > today %}
            <p>{{ per.name }} is from the future!!<p>
        {% endif %}
    {% endfor %}
    
  4. I could also add a property (or even a cached_property) to the model:

    # file 'models.py'
    from django.db import models
    from django.utils import timezone as tz
    from django.utils.functional import cached_property
    
    class Person(models.Model):
        ...
    
        @cached_property
        def is_from_future(self):
            # careful: for long-lived instances do not use 'cached_property' as
            # the time of 'now' might not be right later
            if self.birth_date > tz.localtime(tz.now()).date():
                return True
    
            return False
    
  5. And last but not least, I could just do the processing in the view and add a property to the elements:

    # file 'my_app/views.py'
    from django.utils import timezone as tz
    
    def person_list(request):
        today = tz.localtime(tz.now()).date()
    
        person_list = []
        for p in Person.objects.all():
            p.is_from_future = self.birth_date > today
            person_list.append(p)
    
        return render(request, 'some_template.html', {'person_list': person_list})
    
Sunday, December 25, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :