I have a web application build in Django + Python that interact with web services (written in JAVA).
Now all the database management part is done by web-services i.e. all CRUD operations to actual database is done by web-services.
Now i have to track all User Activities done on my website in some log table.
Like If User posted a new article, then a new row is created into Articles table by web-services and side by side, i need to add a new row into log table , something like "User : Raman has posted a new article (with ID, title etc)"
I have to do this for all Objects in my database like "Article", "Media", "Comments" etc
Note : I am using PostgreSQL
So what is the best way to achieve this..?? (Should I do it in PostgreSQL OR JAVA ..??..And How..??)
So, you have UI <-> Web Services <-> DB
Since the web services talk to the DB, and the web services contain the business logic (i.e. I guess you validate stuff there, create your queries and execute them), then the best place to 'log' activities is in the services themselves.
IMO, logging PostgreSQL transactions is a different thing. It's not the same as logging 'user activities' anymore.
EDIT: This still means you create DB schema for 'logs' and write them to DB.
Second EDIT: Catching log worthy events in the UI and then logging them from there might not be the best idea either. You will have to rewrite logging if you ever decide to replace the UI, or for example, write an alternate UI for, say mobile devices, or something else.
For an audit table within the DB itself, have a look at the PL/pgSQL Trigger Audit Example
This logs every INSERT, UPDATE, DELETE into another table.
In your log table you can have various columns, including:
user_id (the user that did the action)
activity_type (the type of activity, such as view or commented_on)
object_id (the actual object that it concerns, such as the Article or Media)
object_type (the type of object; this can be used later, in combination with object_id to lookup the object in the database)
This way, you can keep track of all actions the users do. You'd need to update this table whenever something happens that you wish to track.
Whenever we had to do this, we overrode signals for every model and possible action.
https://docs.djangoproject.com/en/dev/topics/signals/
You can have the signal do whatever you want, from injecting some HTML into the page, to making an entry in the database. They're an excellent tool to learn to use.
I used django-audit-log and I am very satisfied.
Django-audit-log can track multiple models each in it's own additional table. All of these tables are pretty unified, so it should be fairly straightforward to create a SQL view that shows data for all models.
Here is what I've done to track a single model ("Pauza"):
class Pauza(models.Model):
started = models.TimeField(null=True, blank=False)
ended = models.TimeField(null=True, blank=True)
#... more fields ...
audit_log = AuditLog()
If you want changes to show in Django Admin, you can create an unmanaged model (but this is by no means required):
class PauzaAction(models.Model):
started = models.TimeField(null=True, blank=True)
ended = models.TimeField(null=True, blank=True)
#... more fields ...
# fields added by Audit Trail:
action_id = models.PositiveIntegerField(primary_key=True, default=1, blank=True)
action_user = models.ForeignKey(User, null=True, blank=True)
action_date = models.DateTimeField(null=True, blank=True)
action_type = models.CharField(max_length=31, choices=(('I', 'create'), ('U', 'update'), ('D', 'delete'),), null=True, blank=True)
pauza = models.ForeignKey(Pauza, db_column='id', on_delete=models.DO_NOTHING, default=0, null=True, blank=True)
class Meta:
db_table = 'testapp_pauzaauditlogentry'
managed = False
app_label = 'testapp'
Table testapp_pauzaauditlogentry is automatically created by django-audit-log, this merely creates a model for displaying data from it.
It may be a good idea to throw in some rude tamper protection:
class PauzaAction(models.Model):
# ... all like above, plus:
def save(self, *args, **kwargs):
raise Exception('Permission Denied')
def delete(self, *args, **kwargs):
raise Exception('Permission Denied')
As I said, I imagine you could create a SQL view with the four action_ fields and an additional 'action_model' field that could contain varchar references to model itself (maybe just the original table name).
Related
There is a user with the attribute Role, by default TENANT, using a query we set him LANDLORD and in theHOUSE table he adds an apartment with various attributes: description, price, city_id and others. But suddenly this user wanted to remove himself from the status of LANDLORD, delete his apartments from our database and again become justTENANT, how in this case can I delete the information that he has apartments? How to do it, if he has apartments, then they need to be deleted, if not, then just change the user's status to TENANT?
At first there was an idea to assign a zero value, but it seemed strange to me if we just zeroed it out, because then the table will start to get cluttered. There is also a status option: ACTIVE or BANNED, but I don't like this option, because his apartment is still not needed.
The code looks like this:
#PutMapping ("/ {id}")
#PreAuthorize ("hasAuthority ('landlord: write')")
public void TenantPostAdd (#PathVariable (value = "id") Long id) {
User user = userRepository.findById (id) .orElseThrow ();
Role role = Role.TENANT;
user.setRole (role);
House house = houseRepository.findById (id) .orElseThrow ();
house ... // what's here
}
Full Code
To build this level of infrastructure, there are a lot of questions I would have to ask to recommend something. I'd want to see the current database schema as well. Your also requesting the ability to delete which can become problematic. You may want to consider leaving data if you believe that the customer may change roles again. That kind of information is based off of the terms of agreement.
Have you considered building something like this?
Absolute(Numeric) Mode
0 No Permission --- etc...
https://www.guru99.com/file-permissions.html
This could be a prepared statement issue with not the appropriate joins occurring in the statement. I believe you should take another look over your database schema.
I have a Spring application that runs a cron on it. The cron every few minutes gets new data from external API. The data should be stored in a database (MySQL), in place of old data (Old data should be overwritten by new data). The data requires to be overwritten instead of updated. The application itself provides REST API so the client is able to get the data from the database. So there should not be situation that client sees an empty or just a part of data from database because there is an data update.
Currently I've tried deleting whole old data and insert new data but there is a place that a client gets just a part of the data. I've tried it via Spring Data deleteAll and saveAll methods.
#Override
#Transactional
public List<Country> overrideAll(#NonNull Iterable<Country> countries) {
removeAllAndFlush();
List<CountryEntity> countriesToCreate = stream(countries.spliterator(), false)
.map(CountryEntity::from)
.collect(toList());
List<CountryEntity> createdCountries = repository.saveAll(countriesToCreate);
return createdCountries.stream()
.map(CountryEntity::toCountry)
.collect(toList());
}
private void removeAllAndFlush() {
repository.deleteAll();
repository.flush();
}
I also thought about having a temporary table that gets new data and when the data is complete just replace main table with temporary table. Is it a good idea? Any other ideas?
It's a good idea. You can minimize the downtime by working on another table until it's ready and then switch tables quickly by renaming. This will also improve perceived performance by the users because no record needs to be locked like what happens when using UPDATE/DELETE.
In MySQL, you can use RENAME TABLE if you don't have triggers on the table. It allows multiple table renaming at once and it works atomically (i.e. transaction - if any error happens, no change is made). You can use the following for example
RENAME TABLE countries TO countries_old, countries_new TO countries;
DROP TABLE countries_old;
Refer here for more details
https://dev.mysql.com/doc/refman/5.7/en/rename-table.html
I have 4 tables involved in this query.
Campaign - many to one business
Business - one to many client
Client - one to one contact
Contact
In contact there is the field contact_name which is unique. I need to retrieve all campaigns related to contact(via client and business) which campaign field type equals 2.
What is the best way to do it with hibernate?
In SQL is will look like this:
select *
from campaign,contact, business, client
where campaign.type=2
and client.contact_id = contact.contact_id
and contact.name = 'Josh'
and client.business_id = business.business_id
and campaign.campaign_id = business.business_id
I think that the following should work.
from Compaign where Compaign.type=2 and compaign.business.client.contact.contact_name=:name
You can execute native SQL Queries too using createSQLQuery() method of Session.
You can also use Scalar Property to avoid the overhead of using ResultSetMetadata.
You can find more information on this from here
Am I misunderstanding a basic concept of Restful web services? I have an Android app that I am trying to use a Restful PUT. Two Mysql tables Country and StateProvince with countryId a foreign key on StateProvince table.
If I try to do a PUT to StateProvince using the following
<StateProvince><stateName>Victoria</stateName><countryId>1</countryId></StateProvince>
I get the error below. Am I misunderstanding a basic concept regarding foreign keys and Rest?
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseExcepti on
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityCons traintViolationException: Column 'country_id' cannot be null
Error Code: 1048
Call: INSERT INTO state_province (state_id, state_name, country_id) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(com.pezoot.models.StateProvince[ stateId=2 ])
Short Answer: country_id is null, so this looks like a database/persistence issue. You probably didn't set the Country for the StateProvince (or add the StateProvince to the Country - haven't seen your code so I don't know how you're mapping things).
Long Answer:
Why is there an database identifier coming in as part of your HTTP request?
You need to start thinking in terms of URIs and resources - your StateProvince representation should have some kind of link that relates to a country at a particular URI (e.g. <link rel="country" href="/country/1" /> and in your resource class that handles the PUT verb, you need to be able to conver that URI in to a domain object, an entity (as it seems you're using EclipseLink) which you can use some setter method or something on to establish the database relation. The REST relationship and the database relationship are fundamentally different.
It takes practice and careful thinking to handle what seems like a simple concept (HTTP verbage) against your persistence unit. Something that seems straightforward like PUT has nontrivial processing required in order to make it work as REST would expect.
It is tempting to use database identifiers in URIs because it is easy (especially if you use subresources that just happen to magically know who their parent is: e.g. country/1/stateprovince/2) but you have to step back and ask yourself, is it country/1 or is it country/usa - you also have to ask yourself, is the country and state/province really an entity? or is it just a value object? Do you really intend to PUT a State/Province in its entirety?
Thanks guys.
Once again (unsurprisingly) it appears to be a syntax error. When I used the following:
<stateProvince>
<countryId>
<countryId>1</countryId>
</countryId>
<stateName>New South Wales</stateName> </stateProvince>
Hey presto it works. I had failed to embed the countryId as shown above previously (see old code below)
<stateProvince>
<countryId>1</countryId>
<stateName>New South Wales</stateName> </stateProvince>
And thanks Doug - your response is the sort of insight I am seeking. I dont believe I have quite wrapped my head around the use of links as you describe - but I will investigate further now
I'm new to Liferay development in general, so feel free to point out if I'm going about stuff totally the wrong way.
I'm trying to get a DynamicQuery object of all users within a certain group (I'll use this object to further filter another query I'll do against the message board). The User interface seems to have a roleIds property that I might be able to use, since I already know the roleId I'm interested in. But I can't find the proper way to query if roleIds contains a certain value.
Any ideas on what I want to do?
PS: I would have the exact SQL query I could ask directly, but I'd rather use Liferay's own connection pool, without needing to do some weird ext project thingy.
You don't need a DynamicQuery. These are the methods you are looking for in the classes that Dirk points out:
long[] UserServiceUtil.getRoleUserIds(long roleId)
or
long[] UserLocalServiceUtil.getRoleUserIds(long roleId)
List<User> UserLocalServiceUtil.getRoleUsers(long roleId)
Remember that the methods in the classes XXXLocalServiceUtil are not checking the permissions of the current user.
EDIT: If you are looking for all users with a given role within a given community:
long companyId= _X_; //Perhaps CompanyThreadLocal.getCompanyId() if you don't have it anywhere else?
Role role=RoleLocalServiceUtil.getRole(companyId, "Example Role");
Group group=GroupLocalServiceUtil.getGroup(companyId, "Example Community");
List<UserGroupRole> userGroupRoles = UserGroupRoleLocalServiceUtil.
getUserGroupRolesByGroupAndRole(groupId, role.getRoleId());
for(UserGroupRole userGroupRole:userGroupRoles){
User oneUser=userGroupRole.getUser();
}
The easiest way to access liferays own objects is by using the XXXServiceUtil classes (e.g. RoleServiceUtil.getUserRoles(userId)). Thus you rarely have to deal with any SQL directly. Either the RoleServiceUtil or UserServiceUtil might have what you need.
The roles of an Organizations are stored in the table UserGroupRole, so if you want to get the owner of an Organization you must use the following code:
boolean isOrgOwner =
UserGroupRoleLocalServiceUtil.hasUserGroupRole(
usr.getUserId(),
this.currentOrganization.getGroupId(),
RoleConstants.ORGANIZATION_OWNER);
If you want to retrieve all the Organization Owners of an organization:
List<User> administrators = new LinkedList<>();
List<UserGroupRole> allOrganizationAdministrators =
UserGroupRoleLocalServiceUtil.getUserGroupRolesByGroupAndRole(
this.currentOrganization.getGroupId(), roleId);
for (UserGroupRole userGroupRoleTemp : allOrganizationAdministrators) {
administrators.add(userGroupRoleTemp.getUser());
}
Cheers!